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 static com.android.cts.verifier.notifications.MockListener.JSON_FLAGS;
     20 import static com.android.cts.verifier.notifications.MockListener.JSON_ICON;
     21 import static com.android.cts.verifier.notifications.MockListener.JSON_ID;
     22 import static com.android.cts.verifier.notifications.MockListener.JSON_PACKAGE;
     23 import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
     24 import static com.android.cts.verifier.notifications.MockListener.JSON_WHEN;
     25 
     26 import android.annotation.SuppressLint;
     27 import android.app.Activity;
     28 import android.app.Notification;
     29 import android.app.NotificationManager;
     30 import android.app.PendingIntent;
     31 import android.app.Service;
     32 import android.content.ComponentName;
     33 import android.content.Context;
     34 import android.content.Intent;
     35 import android.content.pm.PackageManager;
     36 import android.os.Bundle;
     37 import android.os.IBinder;
     38 import android.provider.Settings.Secure;
     39 import android.util.Log;
     40 import android.view.LayoutInflater;
     41 import android.view.View;
     42 import android.view.ViewGroup;
     43 import android.widget.Button;
     44 import android.widget.ImageView;
     45 import android.widget.TextView;
     46 
     47 import com.android.cts.verifier.PassFailButtons;
     48 import com.android.cts.verifier.R;
     49 import com.android.cts.verifier.nfc.TagVerifierActivity;
     50 
     51 import org.json.JSONException;
     52 import org.json.JSONObject;
     53 
     54 import java.util.HashSet;
     55 import java.util.List;
     56 import java.util.Set;
     57 import java.util.UUID;
     58 import java.util.concurrent.LinkedBlockingQueue;
     59 
     60 public class NotificationListenerVerifierActivity extends PassFailButtons.Activity
     61 implements Runnable {
     62     private static final String TAG = TagVerifierActivity.class.getSimpleName();
     63     private static final String STATE = "state";
     64     private static LinkedBlockingQueue<String> sDeletedQueue = new LinkedBlockingQueue<String>();
     65 
     66     protected static final String LISTENER_PATH = "com.android.cts.verifier/" +
     67             "com.android.cts.verifier.notifications.MockListener";
     68     protected static final int SETUP = 0;
     69     protected static final int PASS = 1;
     70     protected static final int FAIL = 2;
     71     protected static final int WAIT_FOR_USER = 3;
     72     protected static final int CLEARED = 4;
     73     protected static final int READY = 5;
     74     protected static final int RETRY = 6;
     75 
     76     protected static final int NOTIFICATION_ID = 1001;
     77 
     78     protected int mState;
     79     protected int[] mStatus;
     80     protected PackageManager mPackageManager;
     81     protected NotificationManager mNm;
     82     protected Context mContext;
     83     protected Runnable mRunner;
     84     protected View mHandler;
     85     protected String mPackageString;
     86 
     87     private LayoutInflater mInflater;
     88     private ViewGroup mItemList;
     89 
     90     private String mTag1;
     91     private String mTag2;
     92     private String mTag3;
     93     private int mIcon1;
     94     private int mIcon2;
     95     private int mIcon3;
     96     private int mId1;
     97     private int mId2;
     98     private int mId3;
     99     private long mWhen1;
    100     private long mWhen2;
    101     private long mWhen3;
    102     private int mFlag1;
    103     private int mFlag2;
    104     private int mFlag3;
    105 
    106     public static class DismissService extends Service {
    107         @Override
    108         public IBinder onBind(Intent intent) {
    109             return null;
    110         }
    111 
    112         @Override
    113         public void onStart(Intent intent, int startId) {
    114             sDeletedQueue.offer(intent.getAction());
    115         }
    116     }
    117 
    118     @Override
    119     protected void onCreate(Bundle savedInstanceState) {
    120         onCreate(savedInstanceState, R.layout.nls_main);
    121         setInfoResources(R.string.nls_test, R.string.nls_info, -1);
    122     }
    123 
    124     protected void onCreate(Bundle savedInstanceState, int layoutId) {
    125         super.onCreate(savedInstanceState);
    126 
    127         if (savedInstanceState != null) {
    128             mState = savedInstanceState.getInt(STATE, 0);
    129         }
    130         mContext = this;
    131         mRunner = this;
    132         mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    133         mPackageManager = getPackageManager();
    134         mInflater = getLayoutInflater();
    135         View view = mInflater.inflate(layoutId, null);
    136         mItemList = (ViewGroup) view.findViewById(R.id.nls_test_items);
    137         mHandler = mItemList;
    138         createTestItems();
    139         mStatus = new int[mItemList.getChildCount()];
    140         setContentView(view);
    141 
    142         setPassFailButtonClickListeners();
    143         getPassButton().setEnabled(false);
    144     }
    145 
    146     @Override
    147     protected void onSaveInstanceState (Bundle outState) {
    148         outState.putInt(STATE, mState);
    149     }
    150 
    151     @Override
    152     protected void onResume() {
    153         super.onResume();
    154         next();
    155     }
    156 
    157     // Interface Utilities
    158 
    159     protected void createTestItems() {
    160         createNlsSettingsItem(R.string.nls_enable_service);
    161         createAutoItem(R.string.nls_service_started);
    162         createAutoItem(R.string.nls_note_received);
    163         createAutoItem(R.string.nls_payload_intact);
    164         createAutoItem(R.string.nls_clear_one);
    165         createAutoItem(R.string.nls_clear_all);
    166         createNlsSettingsItem(R.string.nls_disable_service);
    167         createAutoItem(R.string.nls_service_stopped);
    168         createAutoItem(R.string.nls_note_missed);
    169     }
    170 
    171     protected void setItemState(int index, boolean passed) {
    172         ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
    173         ImageView status = (ImageView) item.findViewById(R.id.nls_status);
    174         status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
    175         View button = item.findViewById(R.id.nls_action_button);
    176         button.setClickable(false);
    177         button.setEnabled(false);
    178         status.invalidate();
    179     }
    180 
    181     protected void markItemWaiting(int index) {
    182         ViewGroup item = (ViewGroup) mItemList.getChildAt(index);
    183         ImageView status = (ImageView) item.findViewById(R.id.nls_status);
    184         status.setImageResource(R.drawable.fs_warning);
    185         status.invalidate();
    186     }
    187 
    188     protected View createNlsSettingsItem(int messageId) {
    189         return createUserItem(messageId, R.string.nls_start_settings);
    190     }
    191 
    192     protected View createRetryItem(int messageId) {
    193         return createUserItem(messageId, R.string.attention_ready);
    194     }
    195 
    196     protected View createUserItem(int messageId, int actionId) {
    197         View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
    198         TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
    199         instructions.setText(messageId);
    200         Button button = (Button) item.findViewById(R.id.nls_action_button);
    201         button.setText(actionId);
    202         mItemList.addView(item);
    203         button.setTag(actionId);
    204         return item;
    205     }
    206 
    207     protected View createAutoItem(int stringId) {
    208         View item = mInflater.inflate(R.layout.nls_item, mItemList, false);
    209         TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
    210         instructions.setText(stringId);
    211         View button = item.findViewById(R.id.nls_action_button);
    212         button.setVisibility(View.GONE);
    213         mItemList.addView(item);
    214         return item;
    215     }
    216 
    217     // Test management
    218 
    219     public void run() {
    220         while (mState < mStatus.length && mStatus[mState] != WAIT_FOR_USER) {
    221             if (mStatus[mState] == PASS) {
    222                 setItemState(mState, true);
    223                 mState++;
    224             } else if (mStatus[mState] == FAIL) {
    225                 setItemState(mState, false);
    226                 return;
    227             } else {
    228                 break;
    229             }
    230         }
    231 
    232         if (mState < mStatus.length && mStatus[mState] == WAIT_FOR_USER) {
    233             markItemWaiting(mState);
    234         }
    235 
    236         updateStateMachine();
    237     }
    238 
    239     protected void updateStateMachine() {
    240         switch (mState) {
    241             case 0:
    242                 testIsEnabled(mState);
    243                 break;
    244             case 1:
    245                 testIsStarted(mState);
    246                 break;
    247             case 2:
    248                 testNotificationRecieved(mState);
    249                 break;
    250             case 3:
    251                 testDataIntact(mState);
    252                 break;
    253             case 4:
    254                 testDismissOne(mState);
    255                 break;
    256             case 5:
    257                 testDismissAll(mState);
    258                 break;
    259             case 6:
    260                 testIsDisabled(mState);
    261                 break;
    262             case 7:
    263                 testIsStopped(mState);
    264                 break;
    265             case 8:
    266                 testNotificationNotRecieved(mState);
    267                 break;
    268             case 9:
    269                 getPassButton().setEnabled(true);
    270                 mNm.cancelAll();
    271                 break;
    272         }
    273     }
    274 
    275     public void launchSettings() {
    276         startActivity(
    277                 new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
    278     }
    279 
    280     public void actionPressed(View v) {
    281         Object tag = v.getTag();
    282         if (tag instanceof Integer) {
    283             int id = ((Integer) tag).intValue();
    284             if (id == R.string.nls_start_settings) {
    285                 launchSettings();
    286             } else if (id == R.string.attention_ready) {
    287                 mStatus[mState] = READY;
    288                 next();
    289             }
    290         }
    291     }
    292 
    293     protected PendingIntent makeIntent(int code, String tag) {
    294         Intent intent = new Intent(tag);
    295         intent.setComponent(new ComponentName(mContext, DismissService.class));
    296         PendingIntent pi = PendingIntent.getService(mContext, code, intent,
    297                 PendingIntent.FLAG_UPDATE_CURRENT);
    298         return pi;
    299     }
    300 
    301     @SuppressLint("NewApi")
    302     private void sendNotifications() {
    303         mTag1 = UUID.randomUUID().toString();
    304         mTag2 = UUID.randomUUID().toString();
    305         mTag3 = UUID.randomUUID().toString();
    306 
    307         mNm.cancelAll();
    308 
    309         mWhen1 = System.currentTimeMillis() + 1;
    310         mWhen2 = System.currentTimeMillis() + 2;
    311         mWhen3 = System.currentTimeMillis() + 3;
    312 
    313         mIcon1 = R.drawable.fs_good;
    314         mIcon2 = R.drawable.fs_error;
    315         mIcon3 = R.drawable.fs_warning;
    316 
    317         mId1 = NOTIFICATION_ID + 1;
    318         mId2 = NOTIFICATION_ID + 2;
    319         mId3 = NOTIFICATION_ID + 3;
    320 
    321         mPackageString = "com.android.cts.verifier";
    322 
    323         Notification n1 = new Notification.Builder(mContext)
    324         .setContentTitle("ClearTest 1")
    325         .setContentText(mTag1.toString())
    326         .setPriority(Notification.PRIORITY_LOW)
    327         .setSmallIcon(mIcon1)
    328         .setWhen(mWhen1)
    329         .setDeleteIntent(makeIntent(1, mTag1))
    330         .setOnlyAlertOnce(true)
    331         .build();
    332         mNm.notify(mTag1, mId1, n1);
    333         mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
    334 
    335         Notification n2 = new Notification.Builder(mContext)
    336         .setContentTitle("ClearTest 2")
    337         .setContentText(mTag2.toString())
    338         .setPriority(Notification.PRIORITY_HIGH)
    339         .setSmallIcon(mIcon2)
    340         .setWhen(mWhen2)
    341         .setDeleteIntent(makeIntent(2, mTag2))
    342         .setAutoCancel(true)
    343         .build();
    344         mNm.notify(mTag2, mId2, n2);
    345         mFlag2 = Notification.FLAG_AUTO_CANCEL;
    346 
    347         Notification n3 = new Notification.Builder(mContext)
    348         .setContentTitle("ClearTest 3")
    349         .setContentText(mTag3.toString())
    350         .setPriority(Notification.PRIORITY_LOW)
    351         .setSmallIcon(mIcon3)
    352         .setWhen(mWhen3)
    353         .setDeleteIntent(makeIntent(3, mTag3))
    354         .setAutoCancel(true)
    355         .setOnlyAlertOnce(true)
    356         .build();
    357         mNm.notify(mTag3, mId3, n3);
    358         mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
    359     }
    360 
    361     /**
    362      * Return to the state machine to progress through the tests.
    363      */
    364     protected void next() {
    365         mHandler.removeCallbacks(mRunner);
    366         mHandler.post(mRunner);
    367     }
    368 
    369     /**
    370      * Wait for things to settle before returning to the state machine.
    371      */
    372     protected void delay() {
    373         delay(2000);
    374     }
    375 
    376     /**
    377      * Wait for some time.
    378      */
    379     protected void delay(long waitTime) {
    380         mHandler.removeCallbacks(mRunner);
    381         mHandler.postDelayed(mRunner, waitTime);
    382     }
    383 
    384     protected boolean checkEquals(long expected, long actual, String message) {
    385         if (expected == actual) {
    386             return true;
    387         }
    388         logWithStack(String.format(message, expected, actual));
    389         return false;
    390     }
    391 
    392     protected boolean checkEquals(String expected, String actual, String message) {
    393         if (expected.equals(actual)) {
    394             return true;
    395         }
    396         logWithStack(String.format(message, expected, actual));
    397         return false;
    398     }
    399 
    400     protected boolean checkFlagSet(int expected, int actual, String message) {
    401         if ((expected & actual) != 0) {
    402             return true;
    403         }
    404         logWithStack(String.format(message, expected, actual));
    405         return false;
    406     };
    407 
    408     protected void logWithStack(String message) {
    409         Throwable stackTrace = new Throwable();
    410         stackTrace.fillInStackTrace();
    411         Log.e(TAG, message, stackTrace);
    412     }
    413 
    414     // Tests
    415 
    416     private void testIsEnabled(int i) {
    417         // no setup required
    418         Intent settings = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
    419         if (settings.resolveActivity(mPackageManager) == null) {
    420             logWithStack("failed testIsEnabled: no settings activity");
    421             mStatus[i] = FAIL;
    422         } else {
    423             // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
    424             String listeners = Secure.getString(getContentResolver(),
    425                     "enabled_notification_listeners");
    426             if (listeners != null && listeners.contains(LISTENER_PATH)) {
    427                 mStatus[i] = PASS;
    428             } else {
    429                 mStatus[i] = WAIT_FOR_USER;
    430             }
    431         }
    432         next();
    433     }
    434 
    435     private void testIsStarted(final int i) {
    436         if (mStatus[i] == SETUP) {
    437             mStatus[i] = READY;
    438             // wait for the service to start
    439             delay();
    440         } else {
    441             MockListener.probeListenerStatus(mContext,
    442                     new MockListener.StatusCatcher() {
    443                 @Override
    444                 public void accept(int result) {
    445                     if (result == Activity.RESULT_OK) {
    446                         mStatus[i] = PASS;
    447                     } else {
    448                         logWithStack("failed testIsStarted: " + result);
    449                         mStatus[i] = FAIL;
    450                     }
    451                     next();
    452                 }
    453             });
    454         }
    455     }
    456 
    457     private void testNotificationRecieved(final int i) {
    458         if (mStatus[i] == SETUP) {
    459             MockListener.resetListenerData(this);
    460             mStatus[i] = CLEARED;
    461             // wait for intent to move through the system
    462             delay();
    463         } else if (mStatus[i] == CLEARED) {
    464             sendNotifications();
    465             mStatus[i] = READY;
    466             // wait for notifications to move through the system
    467             delay();
    468         } else {
    469             MockListener.probeListenerPosted(mContext,
    470                     new MockListener.StringListResultCatcher() {
    471                 @Override
    472                 public void accept(List<String> result) {
    473                     if (result != null && result.size() > 0 && result.contains(mTag1)) {
    474                         mStatus[i] = PASS;
    475                     } else {
    476                         logWithStack("failed testNotificationRecieved");
    477                         mStatus[i] = FAIL;
    478                     }
    479                     next();
    480                 }});
    481         }
    482     }
    483 
    484     private void testDataIntact(final int i) {
    485         // no setup required
    486         MockListener.probeListenerPayloads(mContext,
    487                 new MockListener.StringListResultCatcher() {
    488             @Override
    489             public void accept(List<String> result) {
    490                 boolean pass = false;
    491                 Set<String> found = new HashSet<String>();
    492                 if (result != null && result.size() > 0) {
    493                     pass = true;
    494                     for(String payloadData : result) {
    495                         try {
    496                             JSONObject payload = new JSONObject(payloadData);
    497                             pass &= checkEquals(mPackageString, payload.getString(JSON_PACKAGE),
    498                                     "data integrity test fail: notification package (%s, %s)");
    499                             String tag = payload.getString(JSON_TAG);
    500                             if (mTag1.equals(tag)) {
    501                                 found.add(mTag1);
    502                                 pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
    503                                         "data integrity test fail: notification icon (%d, %d)");
    504                                 pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
    505                                         "data integrity test fail: notification flags (%d, %d)");
    506                                 pass &= checkEquals(mId1, payload.getInt(JSON_ID),
    507                                         "data integrity test fail: notification ID (%d, %d)");
    508                                 pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
    509                                         "data integrity test fail: notification when (%d, %d)");
    510                             } else if (mTag2.equals(tag)) {
    511                                 found.add(mTag2);
    512                                 pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
    513                                         "data integrity test fail: notification icon (%d, %d)");
    514                                 pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
    515                                         "data integrity test fail: notification flags (%d, %d)");
    516                                 pass &= checkEquals(mId2, payload.getInt(JSON_ID),
    517                                         "data integrity test fail: notification ID (%d, %d)");
    518                                 pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
    519                                         "data integrity test fail: notification when (%d, %d)");
    520                             } else if (mTag3.equals(tag)) {
    521                                 found.add(mTag3);
    522                                 pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
    523                                         "data integrity test fail: notification icon (%d, %d)");
    524                                 pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
    525                                         "data integrity test fail: notification flags (%d, %d)");
    526                                 pass &= checkEquals(mId3, payload.getInt(JSON_ID),
    527                                         "data integrity test fail: notification ID (%d, %d)");
    528                                 pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
    529                                         "data integrity test fail: notification when (%d, %d)");
    530                             } else {
    531                                 pass = false;
    532                                 logWithStack("failed on unexpected notification tag: " + tag);
    533                             }
    534                         } catch (JSONException e) {
    535                             pass = false;
    536                             Log.e(TAG, "failed to unpack data from mocklistener", e);
    537                         }
    538                     }
    539                 }
    540                 pass &= found.size() == 3;
    541                 mStatus[i] = pass ? PASS : FAIL;
    542                 next();
    543             }});
    544     }
    545 
    546     private void testDismissOne(final int i) {
    547         if (mStatus[i] == SETUP) {
    548             MockListener.resetListenerData(this);
    549             mStatus[i] = CLEARED;
    550             // wait for intent to move through the system
    551             delay();
    552         } else if (mStatus[i] == CLEARED) {
    553             MockListener.clearOne(mContext, mTag1, NOTIFICATION_ID + 1);
    554             mStatus[i] = READY;
    555             delay();
    556         } else {
    557             MockListener.probeListenerRemoved(mContext,
    558                     new MockListener.StringListResultCatcher() {
    559                 @Override
    560                 public void accept(List<String> result) {
    561                     if (result != null && result.size() > 0 && result.contains(mTag1)) {
    562                         mStatus[i] = PASS;
    563                         next();
    564                     } else {
    565                         if (mStatus[i] == RETRY) {
    566                             logWithStack("failed testDismissOne");
    567                             mStatus[i] = FAIL;
    568                             next();
    569                         } else {
    570                             logWithStack("failed testDismissOne, once: retrying");
    571                             mStatus[i] = RETRY;
    572                             delay();
    573                         }
    574                     }
    575                 }});
    576         }
    577     }
    578 
    579     private void testDismissAll(final int i) {
    580         if (mStatus[i] == SETUP) {
    581             MockListener.resetListenerData(this);
    582             mStatus[i] = CLEARED;
    583             // wait for intent to move through the system
    584             delay();
    585         } else if (mStatus[i] == CLEARED) {
    586             MockListener.clearAll(mContext);
    587             mStatus[i] = READY;
    588             delay();
    589         } else {
    590             MockListener.probeListenerRemoved(mContext,
    591                     new MockListener.StringListResultCatcher() {
    592                 @Override
    593                 public void accept(List<String> result) {
    594                     if (result != null && result.size() == 2
    595                             && result.contains(mTag2) && result.contains(mTag3)) {
    596                         mStatus[i] = PASS;
    597                         next();
    598                     } else {
    599                         if (mStatus[i] == RETRY) {
    600                             logWithStack("failed testDismissAll");
    601                             mStatus[i] = FAIL;
    602                             next();
    603                         } else {
    604                             logWithStack("failed testDismissAll, once: retrying");
    605                             mStatus[i] = RETRY;
    606                             delay();
    607                         }
    608                     }
    609                 }
    610             });
    611         }
    612     }
    613 
    614     private void testIsDisabled(int i) {
    615         // no setup required
    616         // TODO: find out why Secure.ENABLED_NOTIFICATION_LISTENERS is hidden
    617         String listeners = Secure.getString(getContentResolver(),
    618                 "enabled_notification_listeners");
    619         if (listeners == null || !listeners.contains(LISTENER_PATH)) {
    620             mStatus[i] = PASS;
    621             next();
    622         } else {
    623             mStatus[i] = WAIT_FOR_USER;
    624             delay();
    625         }
    626     }
    627 
    628     private void testIsStopped(final int i) {
    629         if (mStatus[i] == SETUP) {
    630             mStatus[i] = READY;
    631             // wait for the service to start
    632             delay();
    633         } else {
    634             MockListener.probeListenerStatus(mContext,
    635                     new MockListener.StatusCatcher() {
    636                 @Override
    637                 public void accept(int result) {
    638                     if (result == Activity.RESULT_OK) {
    639                         logWithStack("failed testIsStopped");
    640                         mStatus[i] = FAIL;
    641                     } else {
    642                         mStatus[i] = PASS;
    643                     }
    644                     next();
    645                 }
    646             });
    647         }
    648     }
    649 
    650     private void testNotificationNotRecieved(final int i) {
    651         if (mStatus[i] == SETUP) {
    652             MockListener.resetListenerData(this);
    653             mStatus[i] = CLEARED;
    654             // wait for intent to move through the system
    655             delay();
    656         } else if (mStatus[i] == CLEARED) {
    657             // setup for testNotificationRecieved
    658             sendNotifications();
    659             mStatus[i] = READY;
    660             delay();
    661         } else {
    662             MockListener.probeListenerPosted(mContext,
    663                     new MockListener.StringListResultCatcher() {
    664                 @Override
    665                 public void accept(List<String> result) {
    666                     if (result == null || result.size() == 0) {
    667                         mStatus[i] = PASS;
    668                     } else {
    669                         logWithStack("failed testNotificationNotRecieved");
    670                         mStatus[i] = FAIL;
    671                     }
    672                     next();
    673                 }});
    674         }
    675     }
    676 }
    677