Merge "Refactor notification tests to clean them up." into lmp-sprout-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 7b122c9..98044e4 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1111,7 +1111,7 @@
<meta-data android:name="test_category" android:value="@string/test_category_notifications" />
</activity>
- <activity android:name=".notifications.NotificationAttentionManagementVerifierActivity"
+ <activity android:name=".notifications.AttentionManagementVerifierActivity"
android:label="@string/attention_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -1129,7 +1129,8 @@
</intent-filter>
</service>
- <service android:name=".notifications.NotificationListenerVerifierActivity$DismissService"/>
+ <service android:name=".notifications.InteractiveVerifierActivity$DismissService"/>
+
<activity android:name=".security.CAInstallNotificationVerifierActivity"
android:label="@string/cacert_test">
<intent-filter>
diff --git a/apps/CtsVerifier/res/drawable-hdpi/fs_clock.png b/apps/CtsVerifier/res/drawable-hdpi/fs_clock.png
new file mode 100644
index 0000000..209d78e
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/fs_clock.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_stat_alice.png b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_alice.png
new file mode 100644
index 0000000..e4eea4b
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_alice.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_stat_bob.png b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_bob.png
new file mode 100644
index 0000000..c67ff4f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_bob.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-hdpi/ic_stat_charlie.png b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_charlie.png
new file mode 100644
index 0000000..71afa3e
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-hdpi/ic_stat_charlie.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/fs_clock.png b/apps/CtsVerifier/res/drawable-mdpi/fs_clock.png
new file mode 100644
index 0000000..209d78e
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/fs_clock.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_stat_alice.png b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_alice.png
new file mode 100644
index 0000000..3717827
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_alice.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_stat_bob.png b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_bob.png
new file mode 100644
index 0000000..f266312
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_bob.png
Binary files differ
diff --git a/apps/CtsVerifier/res/drawable-mdpi/ic_stat_charlie.png b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_charlie.png
new file mode 100644
index 0000000..49c4b9a
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable-mdpi/ic_stat_charlie.png
Binary files differ
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
new file mode 100644
index 0000000..d8f196a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import static com.android.cts.verifier.notifications.MockListener.JSON_AMBIENT;
+import static com.android.cts.verifier.notifications.MockListener.JSON_MATCHES_ZEN_FILTER;
+import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
+
+import android.app.Notification;
+import android.content.ContentProviderOperation;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.service.notification.NotificationListenerService;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.cts.verifier.R;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AttentionManagementVerifierActivity
+ extends InteractiveVerifierActivity {
+ private static final String TAG = "NoListenerAttentionVerifier";
+
+ private static final String ALICE = "Alice";
+ private static final String ALICE_PHONE = "+16175551212";
+ private static final String ALICE_EMAIL = "alice@_foo._bar";
+ private static final String BOB = "Bob";
+ private static final String BOB_PHONE = "+16505551212";;
+ private static final String BOB_EMAIL = "bob@_foo._bar";
+ private static final String CHARLIE = "Charlie";
+ private static final String CHARLIE_PHONE = "+13305551212";
+ private static final String CHARLIE_EMAIL = "charlie@_foo._bar";
+ private static final int MODE_NONE = 0;
+ private static final int MODE_URI = 1;
+ private static final int MODE_PHONE = 2;
+ private static final int MODE_EMAIL = 3;
+
+ private Uri mAliceUri;
+ private Uri mBobUri;
+ private Uri mCharlieUri;
+
+ @Override
+ int getTitleResource() {
+ return R.string.attention_test;
+ }
+
+ @Override
+ int getInstructionsResource() {
+ return R.string.attention_info;
+ }
+
+ // Test Setup
+
+ @Override
+ protected List<InteractiveTestCase> createTestItems() {
+ List<InteractiveTestCase> tests = new ArrayList<>(17);
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new InsertContactsTest());
+ tests.add(new SetModeNoneTest());
+ tests.add(new NoneInterceptsAllTest());
+ tests.add(new SetModePriorityTest());
+ tests.add(new PriorityInterceptsSomeTest());
+ tests.add(new SetModeAllTest());
+ tests.add(new AllInterceptsNothingTest());
+ tests.add(new DefaultOrderTest());
+ tests.add(new PrioritytOrderTest());
+ tests.add(new InterruptionOrderTest());
+ tests.add(new AmbientBitsTest());
+ tests.add(new LookupUriOrderTest());
+ tests.add(new EmailOrderTest());
+ tests.add(new PhoneOrderTest());
+ tests.add(new DeleteContactsTest());
+ return tests;
+ }
+
+ // Tests
+
+ protected class InsertContactsTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_create_contacts);
+ }
+
+ @Override
+ void setUp() {
+ insertSingleContact(ALICE, ALICE_PHONE, ALICE_EMAIL, true);
+ insertSingleContact(BOB, BOB_PHONE, BOB_EMAIL, false);
+ // charlie is not in contacts
+ status = READY;
+ // wait for insertions to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ mAliceUri = lookupContact(ALICE_PHONE);
+ mBobUri = lookupContact(BOB_PHONE);
+ mCharlieUri = lookupContact(CHARLIE_PHONE);
+
+ status = PASS;
+ if (mAliceUri == null) { status = FAIL; }
+ if (mBobUri == null) { status = FAIL; }
+ if (mCharlieUri != null) { status = FAIL; }
+ next();
+ }
+ }
+
+ protected class DeleteContactsTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_delete_contacts);
+ }
+
+ @Override
+ void test() {
+ final ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
+ operationList.add(ContentProviderOperation.newDelete(mAliceUri).build());
+ operationList.add(ContentProviderOperation.newDelete(mBobUri).build());
+ try {
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
+ status = READY;
+ } catch (RemoteException e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ status = FAIL;
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ status = FAIL;
+ }
+ status = PASS;
+ next();
+ }
+ }
+
+ protected class SetModeNoneTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createRetryItem(parent, R.string.attention_filter_none);
+ }
+
+ @Override
+ void test() {
+ MockListener.probeFilter(mContext,
+ new MockListener.IntegerResultCatcher() {
+ @Override
+ public void accept(int mode) {
+ if (mode == NotificationListenerService.INTERRUPTION_FILTER_NONE) {
+ status = PASS;
+ next();
+ } else {
+ Log.i("SetModeNoneTest", "waiting, current mode is: " + mode);
+ status = WAIT_FOR_USER;
+ }
+ }
+ });
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ protected class NoneInterceptsAllTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_all_are_filtered);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_URI, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerPayloads(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ Set<String> found = new HashSet<String>();
+ if (result == null || result.size() == 0) {
+ status = FAIL;
+ next();
+ return;
+ }
+ boolean pass = true;
+ for (String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ String tag = payload.getString(JSON_TAG);
+ boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= !zen;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !zen;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= !zen;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+
+ }
+
+ protected class SetModeAllTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createRetryItem(parent, R.string.attention_filter_all);
+ }
+
+ @Override
+ void test() {
+ MockListener.probeFilter(mContext,
+ new MockListener.IntegerResultCatcher() {
+ @Override
+ public void accept(int mode) {
+ if (mode == NotificationListenerService.INTERRUPTION_FILTER_ALL) {
+ status = PASS;
+ next();
+ } else {
+ Log.i("SetModeAllTest", "waiting, current mode is: " + mode);
+ status = WAIT_FOR_USER;
+ }
+ }
+ });
+ }
+ }
+
+ protected class AllInterceptsNothingTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_none_are_filtered);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_URI, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerPayloads(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ Set<String> found = new HashSet<String>();
+ if (result == null || result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ String tag = payload.getString(JSON_TAG);
+ boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= zen;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= zen;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= zen;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ protected class SetModePriorityTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createRetryItem(parent, R.string.attention_filter_priority);
+ }
+
+ @Override
+ void test() {
+ MockListener.probeFilter(mContext,
+ new MockListener.IntegerResultCatcher() {
+ @Override
+ public void accept(int mode) {
+ if (mode == NotificationListenerService.INTERRUPTION_FILTER_PRIORITY) {
+ status = PASS;
+ next();
+ } else {
+ Log.i("SetModePriorityTest", "waiting, current mode is: " + mode);
+ status = WAIT_FOR_USER;
+ }
+ }
+ });
+ }
+ }
+
+ protected class PriorityInterceptsSomeTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_some_are_filtered);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_URI, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerPayloads(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ Set<String> found = new HashSet<String>();
+ if (result == null || result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ String tag = payload.getString(JSON_TAG);
+ boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= zen;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !zen;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= !zen;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // ordered by time: C, B, A
+ protected class DefaultOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_default_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_NONE, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankC < rankB && rankB < rankA) {
+ status = PASS;
+ } else {
+ logFail(rankA + ", " + rankB + ", " + rankC);
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // ordered by priority: B, C, A
+ protected class PrioritytOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_priority_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_NONE, true, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankB < rankC && rankC < rankA) {
+ status = PASS;
+ } else {
+ logFail(rankA + ", " + rankB + ", " + rankC);
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // A starts at the top then falls to the bottom
+ protected class InterruptionOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_interruption_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_NONE, false, true);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankA < rankB && rankA < rankC) {
+ status = RETEST;
+ delay(12000);
+ } else {
+ logFail("noisy notification did not sort to top.");
+ status = FAIL;
+ next();
+ }
+ }
+ });
+ delay(); // in case the catcher never returns
+ } else {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankA > rankB && rankA > rankC) {
+ status = PASS;
+ } else {
+ logFail("noisy notification did not fade back into the list.");
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // B & C above the fold, A below
+ protected class AmbientBitsTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_ambient_bit);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_NONE, true, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerPayloads(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ Set<String> found = new HashSet<String>();
+ if (result == null || result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ String tag = payload.getString(JSON_TAG);
+ boolean ambient = payload.getBoolean(JSON_AMBIENT);
+ Log.e(TAG, tag + (ambient ? " is" : " isn't") + " ambient");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= ambient;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !ambient;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= !ambient;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // ordered by contact affinity: A, B, C
+ protected class LookupUriOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_lookup_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_URI, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankA < rankB && rankB < rankC) {
+ status = PASS;
+ } else {
+ logFail(rankA + ", " + rankB + ", " + rankC);
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // ordered by contact affinity: A, B, C
+ protected class EmailOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_email_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_EMAIL, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankA < rankB && rankB < rankC) {
+ status = PASS;
+ } else {
+ logFail(rankA + ", " + rankB + ", " + rankC);
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // ordered by contact affinity: A, B, C
+ protected class PhoneOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_phone_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications(MODE_PHONE, false, false);
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = findTagInKeys(ALICE, orderedKeys);
+ int rankB = findTagInKeys(BOB, orderedKeys);
+ int rankC = findTagInKeys(CHARLIE, orderedKeys);
+ if (rankA < rankB && rankB < rankC) {
+ status = PASS;
+ } else {
+ logFail(rankA + ", " + rankB + ", " + rankC);
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ // Utilities
+
+ // usePriorities true: B, C, A
+ // usePriorities false:
+ // MODE_NONE: C, B, A
+ // otherwise: A, B ,C
+ private void sendNotifications(int annotationMode, boolean usePriorities, boolean noisy) {
+ // TODO(cwren) Fixes flakey tests due to bug 17644321. Remove this line when it is fixed.
+ int baseId = NOTIFICATION_ID + (noisy ? 3 : 0);
+
+ // C, B, A when sorted by time. Times must be in the past.
+ long whenA = System.currentTimeMillis() - 4000000L;
+ long whenB = System.currentTimeMillis() - 2000000L;
+ long whenC = System.currentTimeMillis() - 1000000L;
+
+ // B, C, A when sorted by priorities
+ int priorityA = usePriorities ? Notification.PRIORITY_MIN : Notification.PRIORITY_DEFAULT;
+ int priorityB = usePriorities ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
+ int priorityC = usePriorities ? Notification.PRIORITY_LOW : Notification.PRIORITY_DEFAULT;
+
+ Notification.Builder alice = new Notification.Builder(mContext)
+ .setContentTitle(ALICE)
+ .setContentText(ALICE)
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setPriority(priorityA)
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .setWhen(whenA);
+ alice.setDefaults(noisy ? Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE : 0);
+ addPerson(annotationMode, alice, mAliceUri, ALICE_PHONE, ALICE_EMAIL);
+ mNm.notify(ALICE, baseId + 1, alice.build());
+
+ Notification.Builder bob = new Notification.Builder(mContext)
+ .setContentTitle(BOB)
+ .setContentText(BOB)
+ .setSmallIcon(R.drawable.ic_stat_bob)
+ .setPriority(priorityB)
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .setWhen(whenB);
+ addPerson(annotationMode, bob, mBobUri, BOB_PHONE, BOB_EMAIL);
+ mNm.notify(BOB, baseId + 2, bob.build());
+
+ Notification.Builder charlie = new Notification.Builder(mContext)
+ .setContentTitle(CHARLIE)
+ .setContentText(CHARLIE)
+ .setSmallIcon(R.drawable.ic_stat_charlie)
+ .setPriority(priorityC)
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .setWhen(whenC);
+ addPerson(annotationMode, charlie, mCharlieUri, CHARLIE_PHONE, CHARLIE_EMAIL);
+ mNm.notify(CHARLIE, baseId + 3, charlie.build());
+ }
+
+ private void addPerson(int mode, Notification.Builder note,
+ Uri uri, String phone, String email) {
+ if (mode == MODE_URI && uri != null) {
+ note.addPerson(uri.toString());
+ } else if (mode == MODE_PHONE) {
+ note.addPerson(Uri.fromParts("tel", phone, null).toString());
+ } else if (mode == MODE_EMAIL) {
+ note.addPerson(Uri.fromParts("mailto", email, null).toString());
+ }
+ }
+
+ private void insertSingleContact(String name, String phone, String email, boolean starred) {
+ final ArrayList<ContentProviderOperation> operationList =
+ new ArrayList<ContentProviderOperation>();
+ ContentProviderOperation.Builder builder =
+ ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
+ builder.withValue(ContactsContract.RawContacts.STARRED, starred ? 1 : 0);
+ operationList.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
+ operationList.add(builder.build());
+
+ if (phone != null) {
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
+ builder.withValue(Phone.NUMBER, phone);
+ builder.withValue(ContactsContract.Data.IS_PRIMARY, 1);
+ operationList.add(builder.build());
+ }
+ if (email != null) {
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ builder.withValue(Email.TYPE, Email.TYPE_HOME);
+ builder.withValue(Email.DATA, email);
+ operationList.add(builder.build());
+ }
+
+ try {
+ mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
+ } catch (RemoteException e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ }
+ }
+
+ private Uri lookupContact(String phone) {
+ Cursor c = null;
+ try {
+ Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(phone));
+ String[] projection = new String[] { ContactsContract.Contacts._ID,
+ ContactsContract.Contacts.LOOKUP_KEY };
+ c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
+ int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
+ String lookupKey = c.getString(lookupIdx);
+ long contactId = c.getLong(idIdx);
+ return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return null;
+ }
+
+ /** Search a list of notification keys for a givcen tag. */
+ private int findTagInKeys(String tag, List<String> orderedKeys) {
+ for (int i = 0; i < orderedKeys.size(); i++) {
+ if (orderedKeys.get(i).contains(tag)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
new file mode 100644
index 0000000..d65af80
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.Settings.Secure;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.TagVerifierActivity;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static com.android.cts.verifier.notifications.MockListener.*;
+
+public abstract class InteractiveVerifierActivity extends PassFailButtons.Activity
+ implements Runnable {
+ private static final String TAG = "InteractiveVerifier";
+ private static final String STATE = "state";
+ private static final String STATUS = "status";
+ private static LinkedBlockingQueue<String> sDeletedQueue = new LinkedBlockingQueue<String>();
+ protected static final String LISTENER_PATH = "com.android.cts.verifier/" +
+ "com.android.cts.verifier.notifications.MockListener";
+ protected static final int SETUP = 0;
+ protected static final int READY = 1;
+ protected static final int RETEST = 2;
+ protected static final int PASS = 3;
+ protected static final int FAIL = 4;
+ protected static final int WAIT_FOR_USER = 5;
+
+ protected static final int NOTIFICATION_ID = 1001;
+
+ // TODO remove these once b/10023397 is fixed
+ public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+ public static final String NOTIFICATION_LISTENER_SETTINGS =
+ "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
+
+ protected InteractiveTestCase mCurrentTest;
+ protected PackageManager mPackageManager;
+ protected NotificationManager mNm;
+ protected Context mContext;
+ protected Runnable mRunner;
+ protected View mHandler;
+ protected String mPackageString;
+
+ private LayoutInflater mInflater;
+ private ViewGroup mItemList;
+ private List<InteractiveTestCase> mTestList;
+ private Iterator<InteractiveTestCase> mTestOrder;
+
+ public static class DismissService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ if(intent != null) { sDeletedQueue.offer(intent.getAction()); }
+ }
+ }
+
+ protected abstract class InteractiveTestCase {
+ int status;
+ private View view;
+
+ abstract View inflate(ViewGroup parent);
+ View getView(ViewGroup parent) {
+ if (view == null) {
+ view = inflate(parent);
+ }
+ return view;
+ }
+
+ /** @return true if the test should re-run when the test activity starts. */
+ boolean autoStart() {
+ return false;
+ }
+
+ /** Set status to {@link #READY} to proceed, or {@link #SETUP} to try again. */
+ void setUp() { status = READY; next(); };
+
+ /** Set status to {@link #PASS} or @{link #FAIL} to proceed, or {@link #READY} to retry. */
+ void test() { status = FAIL; next(); };
+
+ /** Do not modify status. */
+ void tearDown() { next(); };
+
+ protected void logFail() {
+ logFail(null);
+ }
+
+ protected void logFail(String message) {
+ logWithStack("failed " + this.getClass().getSimpleName() +
+ ((message == null) ? "" : ": " + message));
+ }
+ }
+
+ abstract int getTitleResource();
+ abstract int getInstructionsResource();
+
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ int savedStateIndex = (savedState == null) ? 0 : savedState.getInt(STATE, 0);
+ int savedStatus = (savedState == null) ? SETUP : savedState.getInt(STATUS, SETUP);
+ Log.i(TAG, "restored state(" + savedStateIndex + "}, status(" + savedStatus + ")");
+ mContext = this;
+ mRunner = this;
+ mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mPackageManager = getPackageManager();
+ mInflater = getLayoutInflater();
+ View view = mInflater.inflate(R.layout.nls_main, null);
+ mItemList = (ViewGroup) view.findViewById(R.id.nls_test_items);
+ mHandler = mItemList;
+ mTestList = new ArrayList<>();
+ mTestList.addAll(createTestItems());
+ for (InteractiveTestCase test: mTestList) {
+ mItemList.addView(test.getView(mItemList));
+ }
+ mTestOrder = mTestList.iterator();
+ for (int i = 0; i < savedStateIndex; i++) {
+ mCurrentTest = mTestOrder.next();
+ mCurrentTest.status = PASS;
+ }
+ mCurrentTest = mTestOrder.next();
+ mCurrentTest.status = savedStatus;
+
+ setContentView(view);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ setInfoResources(getTitleResource(), getInstructionsResource(), -1);
+ }
+
+ @Override
+ protected void onSaveInstanceState (Bundle outState) {
+ final int stateIndex = mTestList.indexOf(mCurrentTest);
+ outState.putInt(STATE, stateIndex);
+ outState.putInt(STATUS, mCurrentTest.status);
+ Log.i(TAG, "saved state(" + stateIndex + "}, status(" + (mCurrentTest.status) + ")");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mCurrentTest.autoStart()) {
+ mCurrentTest.status = READY;
+ }
+ next();
+ }
+
+ // Interface Utilities
+
+ protected void markItem(InteractiveTestCase test) {
+ if (test == null) { return; }
+ View item = test.view;
+ ImageView status = (ImageView) item.findViewById(R.id.nls_status);
+ View button = item.findViewById(R.id.nls_action_button);
+ switch (test.status) {
+ case WAIT_FOR_USER:
+ status.setImageResource(R.drawable.fs_warning);
+ break;
+
+ case SETUP:
+ case READY:
+ case RETEST:
+ status.setImageResource(R.drawable.fs_clock);
+ break;
+
+ case FAIL:
+ status.setImageResource(R.drawable.fs_error);
+ button.setClickable(false);
+ button.setEnabled(false);
+ break;
+
+ case PASS:
+ status.setImageResource(R.drawable.fs_good);
+ button.setClickable(false);
+ button.setEnabled(false);
+ break;
+
+ }
+ status.invalidate();
+ }
+
+ protected View createNlsSettingsItem(ViewGroup parent, int messageId) {
+ return createUserItem(parent, messageId, R.string.nls_start_settings);
+ }
+
+ protected View createRetryItem(ViewGroup parent, int messageId) {
+ return createUserItem(parent, messageId, R.string.attention_ready);
+ }
+
+ protected View createUserItem(ViewGroup parent, int messageId, int actionId) {
+ View item = mInflater.inflate(R.layout.nls_item, parent, false);
+ TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+ instructions.setText(messageId);
+ Button button = (Button) item.findViewById(R.id.nls_action_button);
+ button.setText(actionId);
+ button.setTag(actionId);
+ return item;
+ }
+
+ protected View createAutoItem(ViewGroup parent, int stringId) {
+ View item = mInflater.inflate(R.layout.nls_item, parent, false);
+ TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+ instructions.setText(stringId);
+ View button = item.findViewById(R.id.nls_action_button);
+ button.setVisibility(View.GONE);
+ return item;
+ }
+
+ // Test management
+
+ abstract protected List<InteractiveTestCase> createTestItems();
+
+ public void run() {
+ if (mCurrentTest == null) { return; }
+ markItem(mCurrentTest);
+ switch (mCurrentTest.status) {
+ case SETUP:
+ Log.i(TAG, "running setup for: " + mCurrentTest.getClass().getSimpleName());
+ mCurrentTest.setUp();
+ break;
+
+ case WAIT_FOR_USER:
+ Log.i(TAG, "waiting for user: " + mCurrentTest.getClass().getSimpleName());
+ break;
+
+ case READY:
+ case RETEST:
+ Log.i(TAG, "running test for: " + mCurrentTest.getClass().getSimpleName());
+ mCurrentTest.test();
+ break;
+
+ case FAIL:
+ Log.i(TAG, "FAIL: " + mCurrentTest.getClass().getSimpleName());
+ mCurrentTest = null;
+ break;
+
+ case PASS:
+ Log.i(TAG, "pass for: " + mCurrentTest.getClass().getSimpleName());
+ mCurrentTest.tearDown();
+ if (mTestOrder.hasNext()) {
+ mCurrentTest = mTestOrder.next();
+ Log.i(TAG, "next test is: " + mCurrentTest.getClass().getSimpleName());
+ } else {
+ Log.i(TAG, "no more tests");
+ mCurrentTest = null;
+ getPassButton().setEnabled(true);
+ mNm.cancelAll();
+ }
+ break;
+ }
+ markItem(mCurrentTest);
+ }
+
+ /**
+ * Return to the state machine to progress through the tests.
+ */
+ protected void next() {
+ mHandler.removeCallbacks(mRunner);
+ mHandler.post(mRunner);
+ }
+
+ /**
+ * Wait for things to settle before returning to the state machine.
+ */
+ protected void delay() {
+ delay(3000);
+ }
+
+ /**
+ * Wait for some time.
+ */
+ protected void delay(long waitTime) {
+ mHandler.removeCallbacks(mRunner);
+ mHandler.postDelayed(mRunner, waitTime);
+ }
+
+ // UI callbacks
+
+ public void launchSettings() {
+ startActivity(new Intent(NOTIFICATION_LISTENER_SETTINGS));
+ }
+
+ public void actionPressed(View v) {
+ Object tag = v.getTag();
+ if (tag instanceof Integer) {
+ int id = ((Integer) tag).intValue();
+ if (id == R.string.nls_start_settings) {
+ launchSettings();
+ } else if (id == R.string.attention_ready) {
+ mCurrentTest.status = READY;
+ next();
+ }
+ }
+ }
+
+ // Utilities
+
+ protected PendingIntent makeIntent(int code, String tag) {
+ Intent intent = new Intent(tag);
+ intent.setComponent(new ComponentName(mContext, DismissService.class));
+ PendingIntent pi = PendingIntent.getService(mContext, code, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ return pi;
+ }
+
+ protected boolean checkEquals(long expected, long actual, String message) {
+ if (expected == actual) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ }
+
+ protected boolean checkEquals(String expected, String actual, String message) {
+ if (expected.equals(actual)) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ }
+
+ protected boolean checkFlagSet(int expected, int actual, String message) {
+ if ((expected & actual) != 0) {
+ return true;
+ }
+ logWithStack(String.format(message, expected, actual));
+ return false;
+ };
+
+ protected void logWithStack(String message) {
+ Throwable stackTrace = new Throwable();
+ stackTrace.fillInStackTrace();
+ Log.e(TAG, message, stackTrace);
+ }
+
+ // Common Tests: useful for the side-effects they generate
+
+ protected class IsEnabledTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createNlsSettingsItem(parent, R.string.nls_enable_service);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ Intent settings = new Intent(NOTIFICATION_LISTENER_SETTINGS);
+ if (settings.resolveActivity(mPackageManager) == null) {
+ logFail("no settings activity");
+ status = FAIL;
+ } else {
+ String listeners = Secure.getString(getContentResolver(),
+ ENABLED_NOTIFICATION_LISTENERS);
+ if (listeners != null && listeners.contains(LISTENER_PATH)) {
+ status = PASS;
+ } else {
+ status = WAIT_FOR_USER;
+ }
+ next();
+ }
+ }
+
+ void tearDown() {
+ // wait for the service to start
+ delay();
+ }
+ }
+
+ protected class ServiceStartedTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_service_started);
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerStatus(mContext,
+ new MockListener.StatusCatcher() {
+ @Override
+ public void accept(int result) {
+ if (result == Activity.RESULT_OK) {
+ status = PASS;
+ next();
+ } else {
+ logFail();
+ status = RETEST;
+ delay();
+ }
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index b4863fa..75eaebd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -238,6 +238,7 @@
Log.d(TAG, "removed: " + sbn.getTag());
mRemoved.add(sbn.getTag());
mNotifications.remove(sbn.getKey());
+ mNotificationKeys.remove(sbn.getTag());
onNotificationRankingUpdate(rankingMap);
mNotificationKeys.remove(sbn.getTag());
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAttentionManagementVerifierActivity.java
deleted file mode 100644
index b4e348f..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAttentionManagementVerifierActivity.java
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.notifications;
-
-import static com.android.cts.verifier.notifications.MockListener.JSON_AMBIENT;
-import static com.android.cts.verifier.notifications.MockListener.JSON_MATCHES_ZEN_FILTER;
-import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.content.ContentProviderOperation;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.Settings.Secure;
-import android.service.notification.NotificationListenerService;
-import android.util.Log;
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.TagVerifierActivity;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class NotificationAttentionManagementVerifierActivity
- extends NotificationListenerVerifierActivity {
- private static final String TAG = TagVerifierActivity.class.getSimpleName();
- private static final String ALICE = "Alice";
- private static final String ALICE_PHONE = "+16175551212";
- private static final String ALICE_EMAIL = "alice@_foo._bar";
- private static final String BOB = "Bob";
- private static final String BOB_PHONE = "+16505551212";;
- private static final String BOB_EMAIL = "bob@_foo._bar";
- private static final String CHARLIE = "Charlie";
- private static final String CHARLIE_PHONE = "+13305551212";
- private static final String CHARLIE_EMAIL = "charlie@_foo._bar";
- private static final int MODE_NONE = 0;
- private static final int MODE_URI = 1;
- private static final int MODE_PHONE = 2;
- private static final int MODE_EMAIL = 3;
- private static final int DELAYED_SETUP = CLEARED;
-
- private Uri mAliceUri;
- private Uri mBobUri;
- private Uri mCharlieUri;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState, R.layout.nls_main);
- setInfoResources(R.string.attention_test, R.string.attention_info, -1);
- }
-
- // Test Setup
-
- @Override
- protected void createTestItems() {
- createNlsSettingsItem(R.string.nls_enable_service);
- createAutoItem(R.string.nls_service_started);
- createAutoItem(R.string.attention_create_contacts);
- createRetryItem(R.string.attention_filter_none);
- createAutoItem(R.string.attention_all_are_filtered);
- createRetryItem(R.string.attention_filter_all);
- createAutoItem(R.string.attention_none_are_filtered);
- createAutoItem(R.string.attention_default_order);
- createAutoItem(R.string.attention_interruption_order);
- createAutoItem(R.string.attention_priority_order);
- createAutoItem(R.string.attention_ambient_bit);
- createAutoItem(R.string.attention_lookup_order);
- createAutoItem(R.string.attention_email_order);
- createAutoItem(R.string.attention_phone_order);
- createRetryItem(R.string.attention_filter_priority);
- createAutoItem(R.string.attention_some_are_filtered);
- createAutoItem(R.string.attention_delete_contacts);
- }
-
- // Test management
-
- @Override
- protected void updateStateMachine() {
- switch (mState) {
- case 0:
- testIsEnabled(mState);
- break;
- case 1:
- testIsStarted(mState);
- break;
- case 2:
- testInsertContacts(mState);
- break;
- case 3:
- testModeNone(mState);
- break;
- case 4:
- testNoneInterceptsAll(mState);
- break;
- case 5:
- testModeAll(mState);
- break;
- case 6:
- testAllInterceptsNothing(mState);
- break;
- case 7:
- testDefaultOrder(mState);
- break;
- case 8:
- testInterruptionOrder(mState);
- break;
- case 9:
- testPrioritytOrder(mState);
- break;
- case 10:
- testAmbientBits(mState);
- break;
- case 11:
- testLookupUriOrder(mState);
- break;
- case 12:
- testEmailOrder(mState);
- break;
- case 13:
- testPhoneOrder(mState);
- break;
- case 14:
- testModePriority(mState);
- break;
- case 15:
- testPriorityInterceptsSome(mState);
- break;
- case 16:
- testDeleteContacts(mState);
- break;
- case 17:
- getPassButton().setEnabled(true);
- mNm.cancelAll();
- break;
- }
- }
-
- // usePriorities true: B, C, A
- // usePriorities false:
- // MODE_NONE: C, B, A
- // otherwise: A, B ,C
- private void sendNotifications(int annotationMode, boolean usePriorities, boolean noisy) {
- // TODO(cwren) Fixes flakey tests due to bug 17644321. Remove this line when it is fixed.
- int baseId = NOTIFICATION_ID + (noisy ? 3 : 0);
-
- // C, B, A when sorted by time. Times must be in the past.
- long whenA = System.currentTimeMillis() - 4000000L;
- long whenB = System.currentTimeMillis() - 2000000L;
- long whenC = System.currentTimeMillis() - 1000000L;
-
- // B, C, A when sorted by priorities
- int priorityA = usePriorities ? Notification.PRIORITY_MIN : Notification.PRIORITY_DEFAULT;
- int priorityB = usePriorities ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
- int priorityC = usePriorities ? Notification.PRIORITY_LOW : Notification.PRIORITY_DEFAULT;
-
- Notification.Builder alice = new Notification.Builder(mContext)
- .setContentTitle(ALICE)
- .setContentText(ALICE)
- .setSmallIcon(R.drawable.fs_good)
- .setPriority(priorityA)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .setWhen(whenA);
- alice.setDefaults(noisy ? Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE : 0);
- addPerson(annotationMode, alice, mAliceUri, ALICE_PHONE, ALICE_EMAIL);
- mNm.notify(ALICE, baseId + 1, alice.build());
-
- Notification.Builder bob = new Notification.Builder(mContext)
- .setContentTitle(BOB)
- .setContentText(BOB)
- .setSmallIcon(R.drawable.fs_warning)
- .setPriority(priorityB)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .setWhen(whenB);
- addPerson(annotationMode, bob, mBobUri, BOB_PHONE, BOB_EMAIL);
- mNm.notify(BOB, baseId + 2, bob.build());
-
- Notification.Builder charlie = new Notification.Builder(mContext)
- .setContentTitle(CHARLIE)
- .setContentText(CHARLIE)
- .setSmallIcon(R.drawable.fs_error)
- .setPriority(priorityC)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .setWhen(whenC);
- addPerson(annotationMode, charlie, mCharlieUri, CHARLIE_PHONE, CHARLIE_EMAIL);
- mNm.notify(CHARLIE, baseId + 3, charlie.build());
- }
-
- private void addPerson(int mode, Notification.Builder note,
- Uri uri, String phone, String email) {
- if (mode == MODE_URI && uri != null) {
- note.addPerson(uri.toString());
- } else if (mode == MODE_PHONE) {
- note.addPerson(Uri.fromParts("tel", phone, null).toString());
- } else if (mode == MODE_EMAIL) {
- note.addPerson(Uri.fromParts("mailto", email, null).toString());
- }
- }
-
- // Tests
-
- private void testIsEnabled(int i) {
- // no setup required
- Intent settings = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
- if (settings.resolveActivity(mPackageManager) == null) {
- logWithStack("failed testIsEnabled: no settings activity");
- mStatus[i] = FAIL;
- } else {
- // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
- String listeners = Secure.getString(getContentResolver(),
- "enabled_notification_listeners");
- if (listeners != null && listeners.contains(LISTENER_PATH)) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = WAIT_FOR_USER;
- }
- }
- next();
- }
-
- private void testIsStarted(final int i) {
- if (mStatus[i] == SETUP) {
- mStatus[i] = READY;
- // wait for the service to start
- delay();
- } else {
- MockListener.probeListenerStatus(mContext,
- new MockListener.StatusCatcher() {
- @Override
- public void accept(int result) {
- if (result == Activity.RESULT_OK) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testIsStarted: " + result);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- private void testModeAll(final int i) {
- if (mStatus[i] == READY || mStatus[i] == SETUP) {
- MockListener.probeFilter(mContext,
- new MockListener.IntegerResultCatcher() {
- @Override
- public void accept(int mode) {
- if (mode == NotificationListenerService.INTERRUPTION_FILTER_ALL) {
- mStatus[i] = PASS;
- } else {
- logWithStack("waiting testModeAll: " + mode);
- mStatus[i] = WAIT_FOR_USER;
- }
- next();
- }
- });
- }
- }
-
- private void testModePriority(final int i) {
- if (mStatus[i] == READY || mStatus[i] == SETUP) {
- MockListener.probeFilter(mContext,
- new MockListener.IntegerResultCatcher() {
- @Override
- public void accept(int mode) {
- if (mode == NotificationListenerService.INTERRUPTION_FILTER_PRIORITY) {
- mStatus[i] = PASS;
- } else {
- logWithStack("waiting testModePriority: " + mode);
- mStatus[i] = WAIT_FOR_USER;
- }
- next();
- }
- });
- }
- }
-
- private void testModeNone(final int i) {
- if (mStatus[i] == READY || mStatus[i] == SETUP) {
- MockListener.probeFilter(mContext,
- new MockListener.IntegerResultCatcher() {
- @Override
- public void accept(int mode) {
- if (mode == NotificationListenerService.INTERRUPTION_FILTER_NONE) {
- mStatus[i] = PASS;
- } else {
- logWithStack("waiting testModeNone: " + mode);
- mStatus[i] = WAIT_FOR_USER;
- }
- next();
- }
- });
- }
- }
-
-
- private void insertSingleContact(String name, String phone, String email, boolean starred) {
- final ArrayList<ContentProviderOperation> operationList =
- new ArrayList<ContentProviderOperation>();
- ContentProviderOperation.Builder builder =
- ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
- builder.withValue(ContactsContract.RawContacts.STARRED, starred ? 1 : 0);
- operationList.add(builder.build());
-
- builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
- builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
- builder.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
- builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
- operationList.add(builder.build());
-
- if (phone != null) {
- builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
- builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
- builder.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
- builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
- builder.withValue(Phone.NUMBER, phone);
- builder.withValue(ContactsContract.Data.IS_PRIMARY, 1);
- operationList.add(builder.build());
- }
- if (email != null) {
- builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
- builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
- builder.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
- builder.withValue(Email.TYPE, Email.TYPE_HOME);
- builder.withValue(Email.DATA, email);
- operationList.add(builder.build());
- }
-
- try {
- mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
- } catch (RemoteException e) {
- Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
- } catch (OperationApplicationException e) {
- Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
- }
- }
-
- private Uri lookupContact(String phone) {
- Cursor c = null;
- try {
- Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(phone));
- String[] projection = new String[] { ContactsContract.Contacts._ID,
- ContactsContract.Contacts.LOOKUP_KEY };
- c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
- int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
- String lookupKey = c.getString(lookupIdx);
- long contactId = c.getLong(idIdx);
- return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
- }
- } catch (Throwable t) {
- Log.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
- }
-
- private void testInsertContacts(final int i) {
- if (mStatus[i] == SETUP) {
- insertSingleContact(ALICE, ALICE_PHONE, ALICE_EMAIL, true);
- insertSingleContact(BOB, BOB_PHONE, BOB_EMAIL, false);
- // charlie is not in contacts
- mStatus[i] = READY;
- // wait for insertions to move through the system
- delay();
- } else {
- mAliceUri = lookupContact(ALICE_PHONE);
- mBobUri = lookupContact(BOB_PHONE);
- mCharlieUri = lookupContact(CHARLIE_PHONE);
-
- mStatus[i] = PASS;
- if (mAliceUri == null) { mStatus[i] = FAIL; }
- if (mBobUri == null) { mStatus[i] = FAIL; }
- if (mCharlieUri != null) { mStatus[i] = FAIL; }
- next();
- }
- }
-
- // ordered by time: C, B, A
- private void testDefaultOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_NONE, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankC < rankB && rankB < rankA) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testDefaultOrder : "
- + rankA + ", " + rankB + ", " + rankC);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // ordered by priority: B, C, A
- private void testPrioritytOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_PHONE, true, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankB < rankC && rankC < rankA) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testPrioritytOrder : "
- + rankA + ", " + rankB + ", " + rankC);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // B & C above the fold, A below
- private void testAmbientBits(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_PHONE, true, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerPayloads(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- boolean pass = false;
- Set<String> found = new HashSet<String>();
- if (result != null && result.size() > 0) {
- pass = true;
- for (String payloadData : result) {
- try {
- JSONObject payload = new JSONObject(payloadData);
- String tag = payload.getString(JSON_TAG);
- if (found.contains(tag)) {
- // multiple entries for same notification!
- pass = false;
- } else if (ALICE.equals(tag)) {
- found.add(ALICE);
- pass &= payload.getBoolean(JSON_AMBIENT);
- } else if (BOB.equals(tag)) {
- found.add(BOB);
- pass &= !payload.getBoolean(JSON_AMBIENT);
- } else if (CHARLIE.equals(tag)) {
- found.add(CHARLIE);
- pass &= !payload.getBoolean(JSON_AMBIENT);
- }
- } catch (JSONException e) {
- pass = false;
- Log.e(TAG, "failed to unpack data from mocklistener", e);
- }
- }
- }
- pass &= found.size() == 3;
- mStatus[i] = pass ? PASS : FAIL;
- next();
- }
- });
- }
- }
-
- // ordered by contact affinity: A, B, C
- private void testLookupUriOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_URI, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankA < rankB && rankB < rankC) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testLookupUriOrder : "
- + rankA + ", " + rankB + ", " + rankC);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // ordered by contact affinity: A, B, C
- private void testEmailOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = DELAYED_SETUP;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == DELAYED_SETUP) {
- sendNotifications(MODE_EMAIL, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankA < rankB && rankB < rankC) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testEmailOrder : "
- + rankA + ", " + rankB + ", " + rankC);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // ordered by contact affinity: A, B, C
- private void testPhoneOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_PHONE, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankA < rankB && rankB < rankC) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testPhoneOrder : "
- + rankA + ", " + rankB + ", " + rankC);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // A starts at the top then falls to the bottom
- private void testInterruptionOrder(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_NONE, false, true);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else if (mStatus[i] == READY) {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankA < rankB && rankA < rankC) {
- mStatus[i] = RETRY;
- delay(12000);
- } else {
- logWithStack("noisy notification did not sort to top.");
- mStatus[i] = FAIL;
- next();
- }
- }
- });
- } else if (mStatus[i] == RETRY) {
- MockListener.probeListenerOrder(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> orderedKeys) {
- int rankA = findTagInKeys(ALICE, orderedKeys);
- int rankB = findTagInKeys(BOB, orderedKeys);
- int rankC = findTagInKeys(CHARLIE, orderedKeys);
- if (rankA > rankB && rankA > rankC) {
- mStatus[i] = PASS;
- } else {
- logWithStack("noisy notification did not fade back into the list.");
- mStatus[i] = FAIL;
- }
- next();
- }
- });
- }
- }
-
- // Nothing should be filtered when mode is ALL
- private void testAllInterceptsNothing(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_URI, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerPayloads(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- boolean pass = false;
- Set<String> found = new HashSet<String>();
- if (result != null && result.size() > 0) {
- pass = true;
- for (String payloadData : result) {
- try {
- JSONObject payload = new JSONObject(payloadData);
- String tag = payload.getString(JSON_TAG);
- if (found.contains(tag)) {
- // multiple entries for same notification!
- pass = false;
- } else if (ALICE.equals(tag)) {
- found.add(ALICE);
- pass &= payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (BOB.equals(tag)) {
- found.add(BOB);
- pass &= payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (CHARLIE.equals(tag)) {
- found.add(CHARLIE);
- pass &= payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- }
- } catch (JSONException e) {
- pass = false;
- Log.e(TAG, "failed to unpack data from mocklistener", e);
- }
- }
- }
- pass &= found.size() == 3;
- mStatus[i] = pass ? PASS : FAIL;
- next();
- }
- });
- }
- }
-
- // A should be filtered when mode is Priority/Starred.
- private void testPriorityInterceptsSome(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_URI, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerPayloads(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- boolean pass = false;
- Set<String> found = new HashSet<String>();
- if (result != null && result.size() > 0) {
- pass = true;
- for (String payloadData : result) {
- try {
- JSONObject payload = new JSONObject(payloadData);
- String tag = payload.getString(JSON_TAG);
- if (found.contains(tag)) {
- // multiple entries for same notification!
- pass = false;
- } else if (ALICE.equals(tag)) {
- found.add(ALICE);
- pass &= payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (BOB.equals(tag)) {
- found.add(BOB);
- pass &= !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (CHARLIE.equals(tag)) {
- found.add(CHARLIE);
- pass &= !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- }
- } catch (JSONException e) {
- pass = false;
- Log.e(TAG, "failed to unpack data from mocklistener", e);
- }
- }
- }
- pass &= found.size() == 3;
- mStatus[i] = pass ? PASS : FAIL;
- next();
- }
- });
- }
- }
-
- // Nothing should get through when mode is None.
- private void testNoneInterceptsAll(final int i) {
- if (mStatus[i] == SETUP) {
- mNm.cancelAll();
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- sendNotifications(MODE_URI, false, false);
- mStatus[i] = READY;
- // wait for notifications to move through the system
- delay();
- } else {
- MockListener.probeListenerPayloads(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- boolean pass = false;
- Set<String> found = new HashSet<String>();
- if (result != null && result.size() > 0) {
- pass = true;
- for (String payloadData : result) {
- try {
- JSONObject payload = new JSONObject(payloadData);
- String tag = payload.getString(JSON_TAG);
- if (found.contains(tag)) {
- // multiple entries for same notification!
- pass = false;
- } else if (ALICE.equals(tag)) {
- found.add(ALICE);
- pass &= !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (BOB.equals(tag)) {
- found.add(BOB);
- pass &= !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- } else if (CHARLIE.equals(tag)) {
- found.add(CHARLIE);
- pass &= !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- }
- } catch (JSONException e) {
- pass = false;
- Log.e(TAG, "failed to unpack data from mocklistener", e);
- }
- }
- }
- pass &= found.size() == 3;
- mStatus[i] = pass ? PASS : FAIL;
- next();
- }
- });
- }
- }
-
- /** Search a list of notification keys for a givcen tag. */
- private int findTagInKeys(String tag, List<String> orderedKeys) {
- for (int i = 0; i < orderedKeys.size(); i++) {
- if (orderedKeys.get(i).contains(tag)) {
- return i;
- }
- }
- return -1;
- }
-
- private void testDeleteContacts(final int i) {
- if (mStatus[i] == SETUP) {
- final ArrayList<ContentProviderOperation> operationList =
- new ArrayList<ContentProviderOperation>();
- operationList.add(ContentProviderOperation.newDelete(mAliceUri).build());
- operationList.add(ContentProviderOperation.newDelete(mBobUri).build());
- try {
- mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
- mStatus[i] = READY;
- } catch (RemoteException e) {
- Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
- mStatus[i] = FAIL;
- } catch (OperationApplicationException e) {
- Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
- mStatus[i] = FAIL;
- }
- // wait for deletions to move through the system
- delay(3000);
- } else if (mStatus[i] == READY) {
- mAliceUri = lookupContact(ALICE_PHONE);
- mBobUri = lookupContact(BOB_PHONE);
- mCharlieUri = lookupContact(CHARLIE_PHONE);
-
- mStatus[i] = PASS;
- if (mAliceUri != null) { mStatus[i] = FAIL; }
- if (mBobUri != null) { mStatus[i] = FAIL; }
- if (mCharlieUri != null) { mStatus[i] = FAIL; }
- next();
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 0ef595b..ace194c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -16,76 +16,30 @@
package com.android.cts.verifier.notifications;
-import static com.android.cts.verifier.notifications.MockListener.JSON_FLAGS;
-import static com.android.cts.verifier.notifications.MockListener.JSON_ICON;
-import static com.android.cts.verifier.notifications.MockListener.JSON_ID;
-import static com.android.cts.verifier.notifications.MockListener.JSON_PACKAGE;
-import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
-import static com.android.cts.verifier.notifications.MockListener.JSON_WHEN;
-
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.IBinder;
import android.provider.Settings.Secure;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.TagVerifierActivity;
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.LinkedBlockingQueue;
-public class NotificationListenerVerifierActivity extends PassFailButtons.Activity
-implements Runnable {
- private static final String TAG = TagVerifierActivity.class.getSimpleName();
- private static final String STATE = "state";
- private static LinkedBlockingQueue<String> sDeletedQueue = new LinkedBlockingQueue<String>();
+import static com.android.cts.verifier.notifications.MockListener.*;
- protected static final String LISTENER_PATH = "com.android.cts.verifier/" +
- "com.android.cts.verifier.notifications.MockListener";
- protected static final int SETUP = 0;
- protected static final int PASS = 1;
- protected static final int FAIL = 2;
- protected static final int WAIT_FOR_USER = 3;
- protected static final int CLEARED = 4;
- protected static final int READY = 5;
- protected static final int RETRY = 6;
-
- protected static final int NOTIFICATION_ID = 1001;
-
- protected int mState;
- protected int[] mStatus;
- protected PackageManager mPackageManager;
- protected NotificationManager mNm;
- protected Context mContext;
- protected Runnable mRunner;
- protected View mHandler;
- protected String mPackageString;
-
- private LayoutInflater mInflater;
- private ViewGroup mItemList;
+public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity
+ implements Runnable {
+ private static final String TAG = "NoListenerVerifier";
private String mTag1;
private String mTag2;
@@ -103,199 +57,31 @@
private int mFlag2;
private int mFlag3;
- public static class DismissService extends Service {
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- sDeletedQueue.offer(intent.getAction());
- }
+ @Override
+ int getTitleResource() {
+ return R.string.nls_test;
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
- onCreate(savedInstanceState, R.layout.nls_main);
- setInfoResources(R.string.nls_test, R.string.nls_info, -1);
+ int getInstructionsResource() {
+ return R.string.nls_info;
}
- protected void onCreate(Bundle savedInstanceState, int layoutId) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null) {
- mState = savedInstanceState.getInt(STATE, 0);
- }
- mContext = this;
- mRunner = this;
- mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- mPackageManager = getPackageManager();
- mInflater = getLayoutInflater();
- View view = mInflater.inflate(layoutId, null);
- mItemList = (ViewGroup) view.findViewById(R.id.nls_test_items);
- mHandler = mItemList;
- createTestItems();
- mStatus = new int[mItemList.getChildCount()];
- setContentView(view);
-
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- }
+ // Test Setup
@Override
- protected void onSaveInstanceState (Bundle outState) {
- outState.putInt(STATE, mState);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- next();
- }
-
- // Interface Utilities
-
- protected void createTestItems() {
- createNlsSettingsItem(R.string.nls_enable_service);
- createAutoItem(R.string.nls_service_started);
- createAutoItem(R.string.nls_note_received);
- createAutoItem(R.string.nls_payload_intact);
- createAutoItem(R.string.nls_clear_one);
- createAutoItem(R.string.nls_clear_all);
- createNlsSettingsItem(R.string.nls_disable_service);
- createAutoItem(R.string.nls_service_stopped);
- createAutoItem(R.string.nls_note_missed);
- }
-
- protected void setItemState(int index, boolean passed) {
- ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
- ImageView status = (ImageView) item.findViewById(R.id.nls_status);
- status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
- View button = item.findViewById(R.id.nls_action_button);
- button.setClickable(false);
- button.setEnabled(false);
- status.invalidate();
- }
-
- protected void markItemWaiting(int index) {
- ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
- ImageView status = (ImageView) item.findViewById(R.id.nls_status);
- status.setImageResource(R.drawable.fs_warning);
- status.invalidate();
- }
-
- protected View createNlsSettingsItem(int messageId) {
- return createUserItem(messageId, R.string.nls_start_settings);
- }
-
- protected View createRetryItem(int messageId) {
- return createUserItem(messageId, R.string.attention_ready);
- }
-
- protected View createUserItem(int messageId, int actionId) {
- View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
- TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
- instructions.setText(messageId);
- Button button = (Button) item.findViewById(R.id.nls_action_button);
- button.setText(actionId);
- mItemList.addView(item);
- button.setTag(actionId);
- return item;
- }
-
- protected View createAutoItem(int stringId) {
- View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
- TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
- instructions.setText(stringId);
- View button = item.findViewById(R.id.nls_action_button);
- button.setVisibility(View.GONE);
- mItemList.addView(item);
- return item;
- }
-
- // Test management
-
- public void run() {
- while (mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
- if (mStatus[mState] == PASS) {
- setItemState(mState, true);
- mState++;
- } else if (mStatus[mState] == FAIL) {
- setItemState(mState, false);
- return;
- } else {
- break;
- }
- }
-
- if (mState < mStatus.length && mStatus[mState] == WAIT_FOR_USER) {
- markItemWaiting(mState);
- }
-
- updateStateMachine();
- }
-
- protected void updateStateMachine() {
- switch (mState) {
- case 0:
- testIsEnabled(mState);
- break;
- case 1:
- testIsStarted(mState);
- break;
- case 2:
- testNotificationRecieved(mState);
- break;
- case 3:
- testDataIntact(mState);
- break;
- case 4:
- testDismissOne(mState);
- break;
- case 5:
- testDismissAll(mState);
- break;
- case 6:
- testIsDisabled(mState);
- break;
- case 7:
- testIsStopped(mState);
- break;
- case 8:
- testNotificationNotRecieved(mState);
- break;
- case 9:
- getPassButton().setEnabled(true);
- mNm.cancelAll();
- break;
- }
- }
-
- public void launchSettings() {
- startActivity(
- new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
- }
-
- public void actionPressed(View v) {
- Object tag = v.getTag();
- if (tag instanceof Integer) {
- int id = ((Integer) tag).intValue();
- if (id == R.string.nls_start_settings) {
- launchSettings();
- } else if (id == R.string.attention_ready) {
- mStatus[mState] = READY;
- next();
- }
- }
- }
-
- protected PendingIntent makeIntent(int code, String tag) {
- Intent intent = new Intent(tag);
- intent.setComponent(new ComponentName(mContext, DismissService.class));
- PendingIntent pi = PendingIntent.getService(mContext, code, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
+ protected List<InteractiveTestCase> createTestItems() {
+ List<InteractiveTestCase> tests = new ArrayList<>(9);
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new NotificationRecievedTest());
+ tests.add(new DataIntactTest());
+ tests.add(new DismissOneTest());
+ tests.add(new DismissAllTest());
+ tests.add(new IsDisabledTest());
+ tests.add(new ServiceStoppedTest());
+ tests.add(new NotificationNotReceivedTest());
+ return tests;
}
@SuppressLint("NewApi")
@@ -310,9 +96,9 @@
mWhen2 = System.currentTimeMillis() + 2;
mWhen3 = System.currentTimeMillis() + 3;
- mIcon1 = R.drawable.fs_good;
- mIcon2 = R.drawable.fs_error;
- mIcon3 = R.drawable.fs_warning;
+ mIcon1 = R.drawable.ic_stat_alice;
+ mIcon2 = R.drawable.ic_stat_bob;
+ mIcon3 = R.drawable.ic_stat_charlie;
mId1 = NOTIFICATION_ID + 1;
mId2 = NOTIFICATION_ID + 2;
@@ -321,356 +107,352 @@
mPackageString = "com.android.cts.verifier";
Notification n1 = new Notification.Builder(mContext)
- .setContentTitle("ClearTest 1")
- .setContentText(mTag1.toString())
- .setPriority(Notification.PRIORITY_LOW)
- .setSmallIcon(mIcon1)
- .setWhen(mWhen1)
- .setDeleteIntent(makeIntent(1, mTag1))
- .setOnlyAlertOnce(true)
- .build();
+ .setContentTitle("ClearTest 1")
+ .setContentText(mTag1.toString())
+ .setPriority(Notification.PRIORITY_LOW)
+ .setSmallIcon(mIcon1)
+ .setWhen(mWhen1)
+ .setDeleteIntent(makeIntent(1, mTag1))
+ .setOnlyAlertOnce(true)
+ .build();
mNm.notify(mTag1, mId1, n1);
mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
Notification n2 = new Notification.Builder(mContext)
- .setContentTitle("ClearTest 2")
- .setContentText(mTag2.toString())
- .setPriority(Notification.PRIORITY_HIGH)
- .setSmallIcon(mIcon2)
- .setWhen(mWhen2)
- .setDeleteIntent(makeIntent(2, mTag2))
- .setAutoCancel(true)
- .build();
+ .setContentTitle("ClearTest 2")
+ .setContentText(mTag2.toString())
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setSmallIcon(mIcon2)
+ .setWhen(mWhen2)
+ .setDeleteIntent(makeIntent(2, mTag2))
+ .setAutoCancel(true)
+ .build();
mNm.notify(mTag2, mId2, n2);
mFlag2 = Notification.FLAG_AUTO_CANCEL;
Notification n3 = new Notification.Builder(mContext)
- .setContentTitle("ClearTest 3")
- .setContentText(mTag3.toString())
- .setPriority(Notification.PRIORITY_LOW)
- .setSmallIcon(mIcon3)
- .setWhen(mWhen3)
- .setDeleteIntent(makeIntent(3, mTag3))
- .setAutoCancel(true)
- .setOnlyAlertOnce(true)
- .build();
+ .setContentTitle("ClearTest 3")
+ .setContentText(mTag3.toString())
+ .setPriority(Notification.PRIORITY_LOW)
+ .setSmallIcon(mIcon3)
+ .setWhen(mWhen3)
+ .setDeleteIntent(makeIntent(3, mTag3))
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .build();
mNm.notify(mTag3, mId3, n3);
mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
}
- /**
- * Return to the state machine to progress through the tests.
- */
- protected void next() {
- mHandler.removeCallbacks(mRunner);
- mHandler.post(mRunner);
- }
-
- /**
- * Wait for things to settle before returning to the state machine.
- */
- protected void delay() {
- delay(2000);
- }
-
- /**
- * Wait for some time.
- */
- protected void delay(long waitTime) {
- mHandler.removeCallbacks(mRunner);
- mHandler.postDelayed(mRunner, waitTime);
- }
-
- protected boolean checkEquals(long expected, long actual, String message) {
- if (expected == actual) {
- return true;
- }
- logWithStack(String.format(message, expected, actual));
- return false;
- }
-
- protected boolean checkEquals(String expected, String actual, String message) {
- if (expected.equals(actual)) {
- return true;
- }
- logWithStack(String.format(message, expected, actual));
- return false;
- }
-
- protected boolean checkFlagSet(int expected, int actual, String message) {
- if ((expected & actual) != 0) {
- return true;
- }
- logWithStack(String.format(message, expected, actual));
- return false;
- };
-
- protected void logWithStack(String message) {
- Throwable stackTrace = new Throwable();
- stackTrace.fillInStackTrace();
- Log.e(TAG, message, stackTrace);
- }
-
// Tests
- private void testIsEnabled(int i) {
- // no setup required
- Intent settings = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
- if (settings.resolveActivity(mPackageManager) == null) {
- logWithStack("failed testIsEnabled: no settings activity");
- mStatus[i] = FAIL;
- } else {
- // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
- String listeners = Secure.getString(getContentResolver(),
- "enabled_notification_listeners");
- if (listeners != null && listeners.contains(LISTENER_PATH)) {
- mStatus[i] = PASS;
- } else {
- mStatus[i] = WAIT_FOR_USER;
- }
- }
- next();
- }
+ private class NotificationRecievedTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_note_received);
- private void testIsStarted(final int i) {
- if (mStatus[i] == SETUP) {
- mStatus[i] = READY;
- // wait for the service to start
- delay();
- } else {
- MockListener.probeListenerStatus(mContext,
- new MockListener.StatusCatcher() {
- @Override
- public void accept(int result) {
- if (result == Activity.RESULT_OK) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testIsStarted: " + result);
- mStatus[i] = FAIL;
- }
- next();
- }
- });
}
- }
- private void testNotificationRecieved(final int i) {
- if (mStatus[i] == SETUP) {
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
+ @Override
+ void setUp() {
sendNotifications();
- mStatus[i] = READY;
+ status = READY;
// wait for notifications to move through the system
delay();
- } else {
+ }
+
+ @Override
+ void test() {
MockListener.probeListenerPosted(mContext,
new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result != null && result.size() > 0 && result.contains(mTag1)) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testNotificationRecieved");
- mStatus[i] = FAIL;
- }
- next();
- }});
- }
- }
-
- private void testDataIntact(final int i) {
- // no setup required
- MockListener.probeListenerPayloads(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- boolean pass = false;
- Set<String> found = new HashSet<String>();
- if (result != null && result.size() > 0) {
- pass = true;
- for(String payloadData : result) {
- try {
- JSONObject payload = new JSONObject(payloadData);
- pass &= checkEquals(mPackageString, payload.getString(JSON_PACKAGE),
- "data integrity test fail: notification package (%s, %s)");
- String tag = payload.getString(JSON_TAG);
- if (mTag1.equals(tag)) {
- found.add(mTag1);
- pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
- "data integrity test fail: notification icon (%d, %d)");
- pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
- "data integrity test fail: notification flags (%d, %d)");
- pass &= checkEquals(mId1, payload.getInt(JSON_ID),
- "data integrity test fail: notification ID (%d, %d)");
- pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
- "data integrity test fail: notification when (%d, %d)");
- } else if (mTag2.equals(tag)) {
- found.add(mTag2);
- pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
- "data integrity test fail: notification icon (%d, %d)");
- pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
- "data integrity test fail: notification flags (%d, %d)");
- pass &= checkEquals(mId2, payload.getInt(JSON_ID),
- "data integrity test fail: notification ID (%d, %d)");
- pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
- "data integrity test fail: notification when (%d, %d)");
- } else if (mTag3.equals(tag)) {
- found.add(mTag3);
- pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
- "data integrity test fail: notification icon (%d, %d)");
- pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
- "data integrity test fail: notification flags (%d, %d)");
- pass &= checkEquals(mId3, payload.getInt(JSON_ID),
- "data integrity test fail: notification ID (%d, %d)");
- pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
- "data integrity test fail: notification when (%d, %d)");
+ @Override
+ public void accept(List<String> result) {
+ if (result != null && result.size() > 0 && result.contains(mTag1)) {
+ status = PASS;
} else {
- pass = false;
- logWithStack("failed on unexpected notification tag: " + tag);
+ logFail();
+ status = FAIL;
}
- } catch (JSONException e) {
- pass = false;
- Log.e(TAG, "failed to unpack data from mocklistener", e);
- }
- }
- }
- pass &= found.size() == 3;
- mStatus[i] = pass ? PASS : FAIL;
- next();
- }});
- }
-
- private void testDismissOne(final int i) {
- if (mStatus[i] == SETUP) {
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- MockListener.clearOne(mContext, mTag1, NOTIFICATION_ID + 1);
- mStatus[i] = READY;
- delay();
- } else {
- MockListener.probeListenerRemoved(mContext,
- new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result != null && result.size() > 0 && result.contains(mTag1)) {
- mStatus[i] = PASS;
- next();
- } else {
- if (mStatus[i] == RETRY) {
- logWithStack("failed testDismissOne");
- mStatus[i] = FAIL;
next();
- } else {
- logWithStack("failed testDismissOne, once: retrying");
- mStatus[i] = RETRY;
- delay();
}
- }
- }});
+ });
+ delay(); // in case the catcher never returns
}
}
- private void testDismissAll(final int i) {
- if (mStatus[i] == SETUP) {
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- MockListener.clearAll(mContext);
- mStatus[i] = READY;
- delay();
- } else {
- MockListener.probeListenerRemoved(mContext,
+ private class DataIntactTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_payload_intact);
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerPayloads(mContext,
new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result != null && result.size() == 2
- && result.contains(mTag2) && result.contains(mTag3)) {
- mStatus[i] = PASS;
- next();
- } else {
- if (mStatus[i] == RETRY) {
- logWithStack("failed testDismissAll");
- mStatus[i] = FAIL;
+ @Override
+ public void accept(List<String> result) {
+ Set<String> found = new HashSet<String>();
+ if (result == null || result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (String payloadData : result) {
+ try {
+ JSONObject payload = new JSONObject(payloadData);
+ pass &= checkEquals(mPackageString,
+ payload.getString(JSON_PACKAGE),
+ "data integrity test: notification package (%s, %s)");
+ String tag = payload.getString(JSON_TAG);
+ if (mTag1.equals(tag)) {
+ found.add(mTag1);
+ pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
+ "data integrity test: notification icon (%d, %d)");
+ pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
+ "data integrity test: notification flags (%d, %d)");
+ pass &= checkEquals(mId1, payload.getInt(JSON_ID),
+ "data integrity test: notification ID (%d, %d)");
+ pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
+ "data integrity test: notification when (%d, %d)");
+ } else if (mTag2.equals(tag)) {
+ found.add(mTag2);
+ pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
+ "data integrity test: notification icon (%d, %d)");
+ pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
+ "data integrity test: notification flags (%d, %d)");
+ pass &= checkEquals(mId2, payload.getInt(JSON_ID),
+ "data integrity test: notification ID (%d, %d)");
+ pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
+ "data integrity test: notification when (%d, %d)");
+ } else if (mTag3.equals(tag)) {
+ found.add(mTag3);
+ pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
+ "data integrity test: notification icon (%d, %d)");
+ pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
+ "data integrity test: notification flags (%d, %d)");
+ pass &= checkEquals(mId3, payload.getInt(JSON_ID),
+ "data integrity test: notification ID (%d, %d)");
+ pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
+ "data integrity test: notification when (%d, %d)");
+ } else {
+ pass = false;
+ logFail("unexpected notification tag: " + tag);
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
next();
- } else {
- logWithStack("failed testDismissAll, once: retrying");
- mStatus[i] = RETRY;
- delay();
}
- }
- }
- });
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
}
}
- private void testIsDisabled(int i) {
- // no setup required
- // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
- String listeners = Secure.getString(getContentResolver(),
- "enabled_notification_listeners");
- if (listeners == null || !listeners.contains(LISTENER_PATH)) {
- mStatus[i] = PASS;
+ private class DismissOneTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_clear_one);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications();
+ status = READY;
+ delay();
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockListener.clearOne(mContext, mTag1, mId1);
+ status = RETEST;
+ } else {
+ MockListener.probeListenerRemoved(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result != null && result.size() != 0
+ && result.contains(mTag1)
+ && !result.contains(mTag2)
+ && !result.contains(mTag3)) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ }
+ delay();
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ private class DismissAllTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_clear_all);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications();
+ status = READY;
+ delay();
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockListener.clearAll(mContext);
+ status = RETEST;
+ } else {
+ MockListener.probeListenerRemoved(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> result) {
+ if (result != null && result.size() != 0
+ && result.contains(mTag1)
+ && result.contains(mTag2)
+ && result.contains(mTag3)) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ }
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ private class IsDisabledTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createNlsSettingsItem(parent, R.string.nls_disable_service);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ String listeners = Secure.getString(getContentResolver(),
+ ENABLED_NOTIFICATION_LISTENERS);
+ if (listeners == null || !listeners.contains(LISTENER_PATH)) {
+ status = PASS;
+ } else {
+ status = WAIT_FOR_USER;
+ }
next();
- } else {
- mStatus[i] = WAIT_FOR_USER;
+ }
+
+ @Override
+ void tearDown() {
+ MockListener.resetListenerData(mContext);
delay();
}
}
- private void testIsStopped(final int i) {
- if (mStatus[i] == SETUP) {
- mStatus[i] = READY;
- // wait for the service to start
- delay();
- } else {
+ private class ServiceStoppedTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_service_stopped);
+ }
+
+ @Override
+ void test() {
MockListener.probeListenerStatus(mContext,
new MockListener.StatusCatcher() {
- @Override
- public void accept(int result) {
- if (result == Activity.RESULT_OK) {
- logWithStack("failed testIsStopped");
- mStatus[i] = FAIL;
- } else {
- mStatus[i] = PASS;
- }
- next();
- }
- });
+ @Override
+ public void accept(int result) {
+ if (result == Activity.RESULT_OK) {
+ logFail();
+ status = FAIL;
+ } else {
+ status = PASS;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ // wait for intent to move through the system
+ delay();
}
}
- private void testNotificationNotRecieved(final int i) {
- if (mStatus[i] == SETUP) {
- MockListener.resetListenerData(this);
- mStatus[i] = CLEARED;
- // wait for intent to move through the system
- delay();
- } else if (mStatus[i] == CLEARED) {
- // setup for testNotificationRecieved
+ private class NotificationNotReceivedTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_note_missed);
+
+ }
+
+ @Override
+ void setUp() {
sendNotifications();
- mStatus[i] = READY;
+ status = READY;
delay();
- } else {
+ }
+
+ @Override
+ void test() {
MockListener.probeListenerPosted(mContext,
new MockListener.StringListResultCatcher() {
- @Override
- public void accept(List<String> result) {
- if (result == null || result.size() == 0) {
- mStatus[i] = PASS;
- } else {
- logWithStack("failed testNotificationNotRecieved");
- mStatus[i] = FAIL;
- }
- next();
- }});
+ @Override
+ public void accept(List<String> result) {
+ if (result == null || result.size() == 0) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ MockListener.resetListenerData(mContext);
+ delay();
}
}
}