Home | History | Annotate | Download | only in notifications
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.notifications;
     18 
     19 import android.annotation.SuppressLint;
     20 import android.app.Activity;
     21 import android.app.Notification;
     22 import android.app.NotificationChannel;
     23 import android.app.NotificationManager;
     24 import android.graphics.Bitmap;
     25 import android.graphics.BitmapFactory;
     26 import android.os.Bundle;
     27 import android.provider.Settings.Secure;
     28 import android.service.notification.StatusBarNotification;
     29 import android.support.v4.app.NotificationCompat;
     30 import android.util.Log;
     31 import android.view.View;
     32 import android.view.ViewGroup;
     33 
     34 import com.android.cts.verifier.R;
     35 
     36 import org.json.JSONException;
     37 import org.json.JSONObject;
     38 
     39 import java.util.ArrayList;
     40 import java.util.HashSet;
     41 import java.util.List;
     42 import java.util.Set;
     43 import java.util.UUID;
     44 
     45 import static com.android.cts.verifier.notifications.MockListener.*;
     46 
     47 import static junit.framework.Assert.assertNotNull;
     48 
     49 public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity
     50         implements Runnable {
     51     private static final String TAG = "NoListenerVerifier";
     52     private static final String NOTIFICATION_CHANNEL_ID = TAG;
     53 
     54     private String mTag1;
     55     private String mTag2;
     56     private String mTag3;
     57     private int mIcon1;
     58     private int mIcon2;
     59     private int mIcon3;
     60     private int mId1;
     61     private int mId2;
     62     private int mId3;
     63     private long mWhen1;
     64     private long mWhen2;
     65     private long mWhen3;
     66     private int mFlag1;
     67     private int mFlag2;
     68     private int mFlag3;
     69 
     70     @Override
     71     int getTitleResource() {
     72         return R.string.nls_test;
     73     }
     74 
     75     @Override
     76     int getInstructionsResource() {
     77         return R.string.nls_info;
     78     }
     79 
     80     // Test Setup
     81 
     82     @Override
     83     protected List<InteractiveTestCase> createTestItems() {
     84         List<InteractiveTestCase> tests = new ArrayList<>(17);
     85         tests.add(new IsEnabledTest());
     86         tests.add(new ServiceStartedTest());
     87         tests.add(new NotificationReceivedTest());
     88         tests.add(new DataIntactTest());
     89         tests.add(new DismissOneTest());
     90         tests.add(new DismissOneWithReasonTest());
     91         tests.add(new DismissAllTest());
     92         tests.add(new SnoozeNotificationForTimeTest());
     93         tests.add(new SnoozeNotificationForTimeCancelTest());
     94         tests.add(new GetSnoozedNotificationTest());
     95         tests.add(new EnableHintsTest());
     96         tests.add(new RequestUnbindTest());
     97         tests.add(new RequestBindTest());
     98         tests.add(new MessageBundleTest());
     99         tests.add(new EnableHintsTest());
    100         tests.add(new IsDisabledTest());
    101         tests.add(new ServiceStoppedTest());
    102         tests.add(new NotificationNotReceivedTest());
    103         return tests;
    104     }
    105 
    106     private void createChannel() {
    107         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
    108                 NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
    109         mNm.createNotificationChannel(channel);
    110     }
    111 
    112     private void deleteChannel() {
    113         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
    114     }
    115 
    116     @SuppressLint("NewApi")
    117     private void sendNotifications() {
    118         mTag1 = UUID.randomUUID().toString();
    119         Log.d(TAG, "Sending " + mTag1);
    120         mTag2 = UUID.randomUUID().toString();
    121         Log.d(TAG, "Sending " + mTag2);
    122         mTag3 = UUID.randomUUID().toString();
    123         Log.d(TAG, "Sending " + mTag3);
    124 
    125         mWhen1 = System.currentTimeMillis() + 1;
    126         mWhen2 = System.currentTimeMillis() + 2;
    127         mWhen3 = System.currentTimeMillis() + 3;
    128 
    129         mIcon1 = R.drawable.ic_stat_alice;
    130         mIcon2 = R.drawable.ic_stat_bob;
    131         mIcon3 = R.drawable.ic_stat_charlie;
    132 
    133         mId1 = NOTIFICATION_ID + 1;
    134         mId2 = NOTIFICATION_ID + 2;
    135         mId3 = NOTIFICATION_ID + 3;
    136 
    137         mPackageString = "com.android.cts.verifier";
    138 
    139         Notification n1 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
    140                 .setContentTitle("ClearTest 1")
    141                 .setContentText(mTag1)
    142                 .setSmallIcon(mIcon1)
    143                 .setWhen(mWhen1)
    144                 .setDeleteIntent(makeIntent(1, mTag1))
    145                 .setOnlyAlertOnce(true)
    146                 .build();
    147         mNm.notify(mTag1, mId1, n1);
    148         mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
    149 
    150         Notification n2 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
    151                 .setContentTitle("ClearTest 2")
    152                 .setContentText(mTag2)
    153                 .setSmallIcon(mIcon2)
    154                 .setWhen(mWhen2)
    155                 .setDeleteIntent(makeIntent(2, mTag2))
    156                 .setAutoCancel(true)
    157                 .build();
    158         mNm.notify(mTag2, mId2, n2);
    159         mFlag2 = Notification.FLAG_AUTO_CANCEL;
    160 
    161         Notification n3 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
    162                 .setContentTitle("ClearTest 3")
    163                 .setContentText(mTag3)
    164                 .setSmallIcon(mIcon3)
    165                 .setWhen(mWhen3)
    166                 .setDeleteIntent(makeIntent(3, mTag3))
    167                 .setAutoCancel(true)
    168                 .setOnlyAlertOnce(true)
    169                 .build();
    170         mNm.notify(mTag3, mId3, n3);
    171         mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
    172     }
    173 
    174     // Tests
    175 
    176     private class NotificationReceivedTest extends InteractiveTestCase {
    177         @Override
    178         View inflate(ViewGroup parent) {
    179             return createAutoItem(parent, R.string.nls_note_received);
    180 
    181         }
    182 
    183         @Override
    184         void setUp() {
    185             createChannel();
    186             sendNotifications();
    187             status = READY;
    188         }
    189 
    190         @Override
    191         void tearDown() {
    192             mNm.cancelAll();
    193             MockListener.getInstance().resetData();
    194             deleteChannel();
    195         }
    196 
    197         @Override
    198         void test() {
    199             List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
    200             if (result.size() > 0 && result.contains(mTag1)) {
    201                 status = PASS;
    202             } else {
    203                 logFail();
    204                 status = FAIL;
    205             }
    206         }
    207     }
    208 
    209     private class DataIntactTest extends InteractiveTestCase {
    210         @Override
    211         View inflate(ViewGroup parent) {
    212             return createAutoItem(parent, R.string.nls_payload_intact);
    213         }
    214 
    215         @Override
    216         void setUp() {
    217             createChannel();
    218             sendNotifications();
    219             status = READY;
    220         }
    221 
    222         @Override
    223         void test() {
    224             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    225 
    226             Set<String> found = new HashSet<String>();
    227             if (result.size() == 0) {
    228                 status = FAIL;
    229                 return;
    230             }
    231             boolean pass = true;
    232             for (JSONObject payload : result) {
    233                 try {
    234                     pass &= checkEquals(mPackageString,
    235                             payload.getString(JSON_PACKAGE),
    236                             "data integrity test: notification package (%s, %s)");
    237                     String tag = payload.getString(JSON_TAG);
    238                     if (mTag1.equals(tag)) {
    239                         found.add(mTag1);
    240                         pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
    241                                 "data integrity test: notification icon (%d, %d)");
    242                         pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
    243                                 "data integrity test: notification flags (%d, %d)");
    244                         pass &= checkEquals(mId1, payload.getInt(JSON_ID),
    245                                 "data integrity test: notification ID (%d, %d)");
    246                         pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
    247                                 "data integrity test: notification when (%d, %d)");
    248                     } else if (mTag2.equals(tag)) {
    249                         found.add(mTag2);
    250                         pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
    251                                 "data integrity test: notification icon (%d, %d)");
    252                         pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
    253                                 "data integrity test: notification flags (%d, %d)");
    254                         pass &= checkEquals(mId2, payload.getInt(JSON_ID),
    255                                 "data integrity test: notification ID (%d, %d)");
    256                         pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
    257                                 "data integrity test: notification when (%d, %d)");
    258                     } else if (mTag3.equals(tag)) {
    259                         found.add(mTag3);
    260                         pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
    261                                 "data integrity test: notification icon (%d, %d)");
    262                         pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
    263                                 "data integrity test: notification flags (%d, %d)");
    264                         pass &= checkEquals(mId3, payload.getInt(JSON_ID),
    265                                 "data integrity test: notification ID (%d, %d)");
    266                         pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
    267                                 "data integrity test: notification when (%d, %d)");
    268                     } else {
    269                         pass = false;
    270                         logFail("unexpected notification tag: " + tag);
    271                     }
    272                 } catch (JSONException e) {
    273                     pass = false;
    274                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    275                 }
    276             }
    277 
    278             pass &= found.size() == 3;
    279             status = pass ? PASS : FAIL;
    280         }
    281 
    282         @Override
    283         void tearDown() {
    284             mNm.cancelAll();
    285             MockListener.getInstance().resetData();
    286             deleteChannel();
    287         }
    288     }
    289 
    290     private class DismissOneTest extends InteractiveTestCase {
    291         @Override
    292         View inflate(ViewGroup parent) {
    293             return createAutoItem(parent, R.string.nls_clear_one);
    294         }
    295 
    296         @Override
    297         void setUp() {
    298             createChannel();
    299             sendNotifications();
    300             status = READY;
    301         }
    302 
    303         @Override
    304         void test() {
    305             if (status == READY) {
    306                 MockListener.getInstance().cancelNotification(
    307                         MockListener.getInstance().getKeyForTag(mTag1));
    308                 status = RETEST;
    309             } else {
    310                 List<String> result = new ArrayList<>(MockListener.getInstance().mRemoved);
    311                 if (result.size() != 0
    312                         && result.contains(mTag1)
    313                         && !result.contains(mTag2)
    314                         && !result.contains(mTag3)) {
    315                     status = PASS;
    316                 } else {
    317                     logFail();
    318                     status = FAIL;
    319                 }
    320             }
    321         }
    322 
    323         @Override
    324         void tearDown() {
    325             mNm.cancelAll();
    326             deleteChannel();
    327             MockListener.getInstance().resetData();
    328         }
    329     }
    330 
    331     private class DismissOneWithReasonTest extends InteractiveTestCase {
    332         int mRetries = 3;
    333 
    334         @Override
    335         View inflate(ViewGroup parent) {
    336             return createAutoItem(parent, R.string.nls_clear_one_reason);
    337         }
    338 
    339         @Override
    340         void setUp() {
    341             createChannel();
    342             sendNotifications();
    343             status = READY;
    344         }
    345 
    346         @Override
    347         void test() {
    348             if (status == READY) {
    349                 MockListener.getInstance().cancelNotification(
    350                         MockListener.getInstance().getKeyForTag(mTag1));
    351                 status = RETEST;
    352             } else {
    353                 List<JSONObject> result =
    354                         new ArrayList<>(MockListener.getInstance().mRemovedReason.values());
    355                 boolean pass = false;
    356                 for (JSONObject payload : result) {
    357                     try {
    358                         pass |= (checkEquals(mTag1,
    359                                 payload.getString(JSON_TAG),
    360                                 "data dismissal test: notification tag (%s, %s)")
    361                                 && checkEquals(REASON_LISTENER_CANCEL,
    362                                 payload.getInt(JSON_REASON),
    363                                 "data dismissal test: reason (%d, %d)"));
    364                         if(pass) {
    365                             break;
    366                         }
    367                     } catch (JSONException e) {
    368                         e.printStackTrace();
    369                     }
    370                 }
    371                 if (pass) {
    372                     status = PASS;
    373                 } else {
    374                     if (--mRetries > 0) {
    375                         sleep(100);
    376                         status = RETEST;
    377                     } else {
    378                         status = FAIL;
    379                     }
    380                 }
    381             }
    382         }
    383 
    384         @Override
    385         void tearDown() {
    386             mNm.cancelAll();
    387             deleteChannel();
    388             MockListener.getInstance().resetData();
    389         }
    390     }
    391 
    392     private class DismissAllTest extends InteractiveTestCase {
    393         @Override
    394         View inflate(ViewGroup parent) {
    395             return createAutoItem(parent, R.string.nls_clear_all);
    396         }
    397 
    398         @Override
    399         void setUp() {
    400             createChannel();
    401             sendNotifications();
    402             status = READY;
    403         }
    404 
    405         @Override
    406         void test() {
    407             if (status == READY) {
    408                 MockListener.getInstance().cancelAllNotifications();
    409                 status = RETEST;
    410             } else {
    411                 List<String> result = new ArrayList<>(MockListener.getInstance().mRemoved);
    412                 if (result.size() != 0
    413                         && result.contains(mTag1)
    414                         && result.contains(mTag2)
    415                         && result.contains(mTag3)) {
    416                     status = PASS;
    417                 } else {
    418                     logFail();
    419                     status = FAIL;
    420                 }
    421             }
    422         }
    423 
    424         @Override
    425         void tearDown() {
    426             mNm.cancelAll();
    427             deleteChannel();
    428             MockListener.getInstance().resetData();
    429         }
    430     }
    431 
    432     private class IsDisabledTest extends InteractiveTestCase {
    433         @Override
    434         View inflate(ViewGroup parent) {
    435             return createNlsSettingsItem(parent, R.string.nls_disable_service);
    436         }
    437 
    438         @Override
    439         boolean autoStart() {
    440             return true;
    441         }
    442 
    443         @Override
    444         void test() {
    445             String listeners = Secure.getString(getContentResolver(),
    446                     ENABLED_NOTIFICATION_LISTENERS);
    447             if (listeners == null || !listeners.contains(LISTENER_PATH)) {
    448                 status = PASS;
    449             } else {
    450                 status = WAIT_FOR_USER;
    451             }
    452         }
    453 
    454         @Override
    455         void tearDown() {
    456             MockListener.getInstance().resetData();
    457         }
    458     }
    459 
    460     private class ServiceStoppedTest extends InteractiveTestCase {
    461         @Override
    462         View inflate(ViewGroup parent) {
    463             return createAutoItem(parent, R.string.nls_service_stopped);
    464         }
    465 
    466         @Override
    467         void test() {
    468             if (mNm.getEffectsSuppressor() == null) {
    469                 status = PASS;
    470             } else {
    471                 status = FAIL;
    472             }
    473         }
    474     }
    475 
    476     private class NotificationNotReceivedTest extends InteractiveTestCase {
    477         @Override
    478         View inflate(ViewGroup parent) {
    479             return createAutoItem(parent, R.string.nls_note_missed);
    480 
    481         }
    482 
    483         @Override
    484         void setUp() {
    485             createChannel();
    486             sendNotifications();
    487             status = READY;
    488         }
    489 
    490         @Override
    491         void test() {
    492             List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
    493             if (result.size() == 0) {
    494                 status = PASS;
    495             } else {
    496                 logFail();
    497                 status = FAIL;
    498             }
    499             next();
    500         }
    501 
    502         @Override
    503         void tearDown() {
    504             mNm.cancelAll();
    505             deleteChannel();
    506             MockListener.getInstance().resetData();
    507         }
    508     }
    509 
    510     private class RequestUnbindTest extends InteractiveTestCase {
    511         int mRetries = 5;
    512 
    513         @Override
    514         View inflate(ViewGroup parent) {
    515             return createAutoItem(parent, R.string.nls_snooze);
    516 
    517         }
    518 
    519         @Override
    520         void setUp() {
    521             status = READY;
    522             MockListener.getInstance().requestInterruptionFilter(
    523                     MockListener.HINT_HOST_DISABLE_CALL_EFFECTS);
    524         }
    525 
    526         @Override
    527         void test() {
    528             if (status == READY) {
    529                 MockListener.getInstance().requestUnbind();
    530                 status = RETEST;
    531             } else {
    532                 if (mNm.getEffectsSuppressor() == null && !MockListener.getInstance().isConnected) {
    533                     status = PASS;
    534                 } else {
    535                     if (--mRetries > 0) {
    536                         status = RETEST;
    537                     } else {
    538                         logFail();
    539                         status = FAIL;
    540                     }
    541                 }
    542                 next();
    543             }
    544         }
    545     }
    546 
    547     private class RequestBindTest extends InteractiveTestCase {
    548         int mRetries = 5;
    549 
    550         @Override
    551         View inflate(ViewGroup parent) {
    552             return createAutoItem(parent, R.string.nls_unsnooze);
    553 
    554         }
    555 
    556         @Override
    557         void test() {
    558             if (status == READY) {
    559                 MockListener.requestRebind(MockListener.COMPONENT_NAME);
    560                 status = RETEST;
    561             } else {
    562                 if (MockListener.getInstance().isConnected) {
    563                     status = PASS;
    564                     next();
    565                 } else {
    566                     if (--mRetries > 0) {
    567                         status = RETEST;
    568                         next();
    569                     } else {
    570                         logFail();
    571                         status = FAIL;
    572                     }
    573                 }
    574             }
    575         }
    576     }
    577 
    578     private class EnableHintsTest extends InteractiveTestCase {
    579         @Override
    580         View inflate(ViewGroup parent) {
    581             return createAutoItem(parent, R.string.nls_hints);
    582 
    583         }
    584 
    585         @Override
    586         void test() {
    587             if (status == READY) {
    588                 MockListener.getInstance().requestListenerHints(
    589                         MockListener.HINT_HOST_DISABLE_CALL_EFFECTS);
    590                 status = RETEST;
    591             } else {
    592                 int result = MockListener.getInstance().getCurrentListenerHints();
    593                 if (result == MockListener.HINT_HOST_DISABLE_CALL_EFFECTS) {
    594                     status = PASS;
    595                     next();
    596                 } else {
    597                     logFail();
    598                     status = FAIL;
    599                 }
    600             }
    601         }
    602     }
    603 
    604     private class SnoozeNotificationForTimeTest extends InteractiveTestCase {
    605         final static int READY_TO_SNOOZE = 0;
    606         final static int SNOOZED = 1;
    607         final static int READY_TO_CHECK_FOR_UNSNOOZE = 2;
    608         int state = -1;
    609         long snoozeTime = 3000;
    610 
    611         @Override
    612         View inflate(ViewGroup parent) {
    613             return createAutoItem(parent, R.string.nls_snooze_one_time);
    614         }
    615 
    616         @Override
    617         void setUp() {
    618             createChannel();
    619             sendNotifications();
    620             status = READY;
    621             state = READY_TO_SNOOZE;
    622             delay();
    623         }
    624 
    625         @Override
    626         void test() {
    627             status = RETEST;
    628             if (state == READY_TO_SNOOZE) {
    629                 MockListener.getInstance().snoozeNotification(
    630                         MockListener.getInstance().getKeyForTag(mTag1), snoozeTime);
    631                 state = SNOOZED;
    632             } else if (state == SNOOZED) {
    633                 List<JSONObject> result =
    634                         new ArrayList<>(MockListener.getInstance().mRemovedReason.values());
    635                 boolean pass = false;
    636                 for (JSONObject payload : result) {
    637                     try {
    638                         pass |= (checkEquals(mTag1,
    639                                 payload.getString(JSON_TAG),
    640                                 "data dismissal test: notification tag (%s, %s)")
    641                                 && checkEquals(MockListener.REASON_SNOOZED,
    642                                 payload.getInt(JSON_REASON),
    643                                 "data dismissal test: reason (%d, %d)"));
    644                         if (pass) {
    645                             break;
    646                         }
    647                     } catch (JSONException e) {
    648                         e.printStackTrace();
    649                     }
    650                 }
    651                 if (!pass) {
    652                     logFail();
    653                     status = FAIL;
    654                     next();
    655                     return;
    656                 } else {
    657                     state = READY_TO_CHECK_FOR_UNSNOOZE;
    658                 }
    659             } else {
    660                 List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
    661                 if (result.size() > 0 && result.contains(mTag1)) {
    662                     status = PASS;
    663                 } else {
    664                     logFail();
    665                     status = FAIL;
    666                 }
    667             }
    668         }
    669 
    670         @Override
    671         void tearDown() {
    672             mNm.cancelAll();
    673             deleteChannel();
    674             MockListener.getInstance().resetData();
    675             delay();
    676         }
    677     }
    678 
    679     /**
    680      * Posts notifications, snoozes one of them. Verifies that it is snoozed. Cancels all
    681      * notifications and reposts them. Confirms that the original notification is still snoozed.
    682      */
    683     private class SnoozeNotificationForTimeCancelTest extends InteractiveTestCase {
    684 
    685         final static int READY_TO_SNOOZE = 0;
    686         final static int SNOOZED = 1;
    687         final static int READY_TO_CHECK_FOR_SNOOZE = 2;
    688         int state = -1;
    689         long snoozeTime = 10000;
    690         private String tag;
    691 
    692         @Override
    693         View inflate(ViewGroup parent) {
    694             return createAutoItem(parent, R.string.nls_snooze_one_time);
    695         }
    696 
    697         @Override
    698         void setUp() {
    699             createChannel();
    700             sendNotifications();
    701             tag = mTag1;
    702             status = READY;
    703             state = READY_TO_SNOOZE;
    704             delay();
    705         }
    706 
    707         @Override
    708         void test() {
    709             status = RETEST;
    710             if (state == READY_TO_SNOOZE) {
    711                 MockListener.getInstance().snoozeNotification(
    712                         MockListener.getInstance().getKeyForTag(tag), snoozeTime);
    713                 state = SNOOZED;
    714             } else if (state == SNOOZED) {
    715                 List<String> result = getSnoozed();
    716                 if (result.size() >= 1
    717                         && result.contains(tag)) {
    718                     // cancel and repost
    719                     sendNotifications();
    720                     state = READY_TO_CHECK_FOR_SNOOZE;
    721                 } else {
    722                     logFail();
    723                     status = FAIL;
    724                 }
    725             } else {
    726                 List<String> result = getSnoozed();
    727                 if (result.size() >= 1
    728                         && result.contains(tag)) {
    729                     status = PASS;
    730                 } else {
    731                     logFail();
    732                     status = FAIL;
    733                 }
    734             }
    735         }
    736 
    737         private List<String> getSnoozed() {
    738             List<String> result = new ArrayList<>();
    739             StatusBarNotification[] snoozed = MockListener.getInstance().getSnoozedNotifications();
    740             for (StatusBarNotification sbn : snoozed) {
    741                 result.add(sbn.getTag());
    742             }
    743             return result;
    744         }
    745 
    746         @Override
    747         void tearDown() {
    748             mNm.cancelAll();
    749             deleteChannel();
    750             MockListener.getInstance().resetData();
    751         }
    752     }
    753 
    754     private class GetSnoozedNotificationTest extends InteractiveTestCase {
    755         final static int READY_TO_SNOOZE = 0;
    756         final static int SNOOZED = 1;
    757         final static int READY_TO_CHECK_FOR_GET_SNOOZE = 2;
    758         int state = -1;
    759         long snoozeTime = 30000;
    760 
    761         @Override
    762         View inflate(ViewGroup parent) {
    763             return createAutoItem(parent, R.string.nls_get_snoozed);
    764         }
    765 
    766         @Override
    767         void setUp() {
    768             createChannel();
    769             sendNotifications();
    770             status = READY;
    771             state = READY_TO_SNOOZE;
    772         }
    773 
    774         @Override
    775         void test() {
    776             status = RETEST;
    777             if (state == READY_TO_SNOOZE) {
    778                 MockListener.getInstance().snoozeNotification(
    779                         MockListener.getInstance().getKeyForTag(mTag1), snoozeTime);
    780                 MockListener.getInstance().snoozeNotification(
    781                         MockListener.getInstance().getKeyForTag(mTag2), snoozeTime);
    782                 state = SNOOZED;
    783             } else if (state == SNOOZED) {
    784                 List<JSONObject> result =
    785                         new ArrayList<>(MockListener.getInstance().mRemovedReason.values());
    786                 if (result.size() == 0) {
    787                     status = FAIL;
    788                     return;
    789                 }
    790                 boolean pass = false;
    791                 for (JSONObject payload : result) {
    792                     try {
    793                         pass |= (checkEquals(mTag1,
    794                                 payload.getString(JSON_TAG),
    795                                 "data dismissal test: notification tag (%s, %s)")
    796                                 && checkEquals(MockListener.REASON_SNOOZED,
    797                                 payload.getInt(JSON_REASON),
    798                                 "data dismissal test: reason (%d, %d)"));
    799                         if (pass) {
    800                             break;
    801                         }
    802                     } catch (JSONException e) {
    803                         e.printStackTrace();
    804                     }
    805                 }
    806                 if (!pass) {
    807                     logFail();
    808                     status = FAIL;
    809                 } else {
    810                     state = READY_TO_CHECK_FOR_GET_SNOOZE;
    811                 }
    812             } else {
    813                 List<String> result = new ArrayList<>();
    814                 StatusBarNotification[] snoozed =
    815                         MockListener.getInstance().getSnoozedNotifications();
    816                 for (StatusBarNotification sbn : snoozed) {
    817                     result.add(sbn.getTag());
    818                 }
    819                 if (result.size() >= 2
    820                         && result.contains(mTag1)
    821                         && result.contains(mTag2)) {
    822                     status = PASS;
    823                 } else {
    824                     logFail();
    825                     status = FAIL;
    826                 }
    827             }
    828         }
    829 
    830         @Override
    831         void tearDown() {
    832             mNm.cancelAll();
    833             deleteChannel();
    834             MockListener.getInstance().resetData();
    835             delay();
    836         }
    837     }
    838 
    839     /** Tests that the extras {@link Bundle} in a MessagingStyle#Message is preserved. */
    840     private class MessageBundleTest extends InteractiveTestCase {
    841         private final String extrasKey1 = "extras_key_1";
    842         private final CharSequence extrasValue1 = "extras_value_1";
    843         private final String extrasKey2 = "extras_key_2";
    844         private final CharSequence extrasValue2 = "extras_value_2";
    845 
    846         @Override
    847         View inflate(ViewGroup parent) {
    848             return createAutoItem(parent, R.string.msg_extras_preserved);
    849         }
    850 
    851         @Override
    852         void setUp() {
    853             createChannel();
    854             sendMessagingNotification();
    855             status = READY;
    856         }
    857 
    858         @Override
    859         void tearDown() {
    860             mNm.cancelAll();
    861             deleteChannel();
    862             delay();
    863         }
    864 
    865         private void sendMessagingNotification() {
    866             mTag1 = UUID.randomUUID().toString();
    867             mNm.cancelAll();
    868             mWhen1 = System.currentTimeMillis() + 1;
    869             mIcon1 = R.drawable.ic_stat_alice;
    870             mId1 = NOTIFICATION_ID + 1;
    871 
    872             Notification.MessagingStyle.Message msg1 =
    873                     new Notification.MessagingStyle.Message("text1", 0 /* timestamp */, "sender1");
    874             msg1.getExtras().putCharSequence(extrasKey1, extrasValue1);
    875 
    876             Notification.MessagingStyle.Message msg2 =
    877                     new Notification.MessagingStyle.Message("text2", 1 /* timestamp */, "sender2");
    878             msg2.getExtras().putCharSequence(extrasKey2, extrasValue2);
    879 
    880             Notification.MessagingStyle style = new Notification.MessagingStyle("display_name");
    881             style.addMessage(msg1);
    882             style.addMessage(msg2);
    883 
    884             Notification n1 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
    885                     .setContentTitle("ClearTest 1")
    886                     .setContentText(mTag1.toString())
    887                     .setPriority(Notification.PRIORITY_LOW)
    888                     .setSmallIcon(mIcon1)
    889                     .setWhen(mWhen1)
    890                     .setDeleteIntent(makeIntent(1, mTag1))
    891                     .setOnlyAlertOnce(true)
    892                     .setStyle(style)
    893                     .build();
    894             mNm.notify(mTag1, mId1, n1);
    895             mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
    896         }
    897 
    898         // Returns true on success.
    899         private boolean verifyMessage(
    900                 NotificationCompat.MessagingStyle.Message message,
    901                 String extrasKey,
    902                 CharSequence extrasValue) {
    903             return message.getExtras() != null
    904                     && message.getExtras().getCharSequence(extrasKey) != null
    905                     && message.getExtras().getCharSequence(extrasKey).equals(extrasValue);
    906         }
    907 
    908         @Override
    909         void test() {
    910             List<Notification> result =
    911                     new ArrayList<>(MockListener.getInstance().mPostedNotifications);
    912             if (result.size() != 1 || result.get(0) == null) {
    913                 logFail();
    914                 status = FAIL;
    915                 next();
    916                 return;
    917             }
    918             // Can only read in MessaginStyle using the compat class.
    919             NotificationCompat.MessagingStyle readStyle =
    920                     NotificationCompat.MessagingStyle
    921                             .extractMessagingStyleFromNotification(
    922                                     result.get(0));
    923             if (readStyle == null || readStyle.getMessages().size() != 2) {
    924                 status = FAIL;
    925                 logFail();
    926                 next();
    927                 return;
    928             }
    929 
    930             if (!verifyMessage(readStyle.getMessages().get(0), extrasKey1,
    931                     extrasValue1)
    932                     || !verifyMessage(
    933                     readStyle.getMessages().get(1), extrasKey2, extrasValue2)) {
    934                 status = FAIL;
    935                 logFail();
    936                 next();
    937                 return;
    938             }
    939 
    940             status = PASS;
    941         }
    942     }
    943 }
    944