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 package com.android.cts.verifier.notifications;
     17 
     18 import android.app.Activity;
     19 import android.app.Notification;
     20 import android.content.BroadcastReceiver;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.os.Bundle;
     26 import android.os.Parcelable;
     27 import android.service.notification.NotificationListenerService;
     28 import android.service.notification.StatusBarNotification;
     29 import android.util.ArrayMap;
     30 import android.util.Log;
     31 
     32 import org.json.JSONException;
     33 import org.json.JSONObject;
     34 
     35 import java.util.ArrayList;
     36 import java.util.HashSet;
     37 import java.util.List;
     38 import java.util.Set;
     39 
     40 public class MockListener extends NotificationListenerService {
     41     static final String TAG = "MockListener";
     42 
     43     public static final ComponentName COMPONENT_NAME =
     44             new ComponentName("com.android.cts.verifier", MockListener.class.getName());
     45 
     46     static final String SERVICE_BASE = "android.service.notification.cts.";
     47     static final String SERVICE_CHECK = SERVICE_BASE + "SERVICE_CHECK";
     48     static final String SERVICE_POSTED = SERVICE_BASE + "SERVICE_POSTED";
     49     static final String SERVICE_PAYLOADS = SERVICE_BASE + "SERVICE_PAYLOADS";
     50     static final String SERVICE_REMOVED = SERVICE_BASE + "SERVICE_REMOVED";
     51     static final String SERVICE_REMOVED_REASON = SERVICE_BASE + "SERVICE_REMOVED";
     52     static final String SERVICE_RESET = SERVICE_BASE + "SERVICE_RESET";
     53     static final String SERVICE_CLEAR_ONE = SERVICE_BASE + "SERVICE_CLEAR_ONE";
     54     static final String SERVICE_CLEAR_ALL = SERVICE_BASE + "SERVICE_CLEAR_ALL";
     55     static final String SERVICE_SNOOZE = SERVICE_BASE + "SERVICE_SNOOZE";
     56     static final String SERVICE_HINTS = SERVICE_BASE + "SERVICE_HINTS";
     57     static final String SERVICE_PROBE_HINTS = SERVICE_BASE + "SERVICE_PROBE_HINTS";
     58     static final String SERVICE_ORDER = SERVICE_BASE + "SERVICE_ORDER";
     59     static final String SERVICE_DND = SERVICE_BASE + "SERVICE_DND";
     60     static final String SERVICE_SNOOZE_DURATION = SERVICE_BASE + "SERVICE_SNOOZE_DURATION";
     61     static final String SERVICE_GET_SNOOZED = SERVICE_BASE + "GET_SNOOZED";
     62 
     63     static final String EXTRA_PAYLOAD = "PAYLOAD";
     64     static final String EXTRA_POSTED_NOTIFICATIONS = "NOTIFICATION_PAYLOAD";
     65     static final String EXTRA_INT = "INT";
     66     static final String EXTRA_TAG = "TAG";
     67     static final String EXTRA_CODE = "CODE";
     68     static final String EXTRA_LONG = "LONG";
     69 
     70     static final int RESULT_NO_SERVER = Activity.RESULT_FIRST_USER + 1;
     71 
     72     public static final String JSON_FLAGS = "flag";
     73     public static final String JSON_ICON = "icon";
     74     public static final String JSON_ID = "id";
     75     public static final String JSON_PACKAGE = "pkg";
     76     public static final String JSON_WHEN = "when";
     77     public static final String JSON_TAG = "tag";
     78     public static final String JSON_RANK = "rank";
     79     public static final String JSON_AMBIENT = "ambient";
     80     public static final String JSON_MATCHES_ZEN_FILTER = "matches_zen_filter";
     81     public static final String JSON_REASON = "reason";
     82     public static final String JSON_HINTS = "hints";
     83 
     84     private ArrayList<String> mPosted = new ArrayList<String>();
     85     private ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
     86     private ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
     87     private ArrayList<String> mRemoved = new ArrayList<String>();
     88     private ArrayMap<String, JSONObject> mRemovedReason = new ArrayMap<>();
     89     private ArrayList<String> mSnoozed = new ArrayList<>();
     90     private ArrayList<String> mOrder = new ArrayList<>();
     91     private Set<String> mTestPackages = new HashSet<>();
     92     private BroadcastReceiver mReceiver;
     93     private int mDND = -1;
     94     private ArrayList<Notification> mPostedNotifications = new ArrayList<Notification>();
     95 
     96     @Override
     97     public void onCreate() {
     98         super.onCreate();
     99         Log.d(TAG, "created");
    100 
    101         mTestPackages.add("com.android.cts.verifier");
    102         mTestPackages.add("com.android.cts.robot");
    103 
    104         mPosted = new ArrayList<String>();
    105         mRemoved = new ArrayList<String>();
    106         mSnoozed = new ArrayList<String>();
    107         mPostedNotifications = new ArrayList<Notification>();
    108 
    109         mReceiver = new BroadcastReceiver() {
    110             @Override
    111             public void onReceive(Context context, Intent intent) {
    112                 final String action = intent.getAction();
    113                 final Bundle bundle = new Bundle();
    114                 Log.d(TAG, action);
    115                 if (SERVICE_CHECK.equals(action)) {
    116                     setResultCode(Activity.RESULT_OK);
    117                 } else if (SERVICE_POSTED.equals(action)) {
    118                     bundle.putStringArrayList(EXTRA_PAYLOAD, mPosted);
    119                     bundle.putParcelableArrayList(EXTRA_POSTED_NOTIFICATIONS, mPostedNotifications);
    120                     setResultExtras(bundle);
    121                     setResultCode(Activity.RESULT_OK);
    122                 } else if (SERVICE_DND.equals(action)) {
    123                     bundle.putInt(EXTRA_INT, mDND);
    124                     setResultExtras(bundle);
    125                     setResultCode(Activity.RESULT_OK);
    126                 } else if (SERVICE_ORDER.equals(action)) {
    127                     bundle.putStringArrayList(EXTRA_PAYLOAD, mOrder);
    128                     setResultExtras(bundle);
    129                     setResultCode(Activity.RESULT_OK);
    130                 } else if (SERVICE_PAYLOADS.equals(action)) {
    131                     ArrayList<String> payloadData = new ArrayList<>(mNotifications.size());
    132                     for (JSONObject payload: mNotifications.values()) {
    133                         payloadData.add(payload.toString());
    134                     }
    135                     bundle.putStringArrayList(EXTRA_PAYLOAD, payloadData);
    136                     setResultExtras(bundle);
    137                     setResultCode(Activity.RESULT_OK);
    138                 } else if (SERVICE_REMOVED.equals(action)) {
    139                     bundle.putStringArrayList(EXTRA_PAYLOAD, mRemoved);
    140                     setResultExtras(bundle);
    141                     setResultCode(Activity.RESULT_OK);
    142                 } else if (SERVICE_REMOVED_REASON.equals(action)) {
    143                     ArrayList<String> payloadData = new ArrayList<>(mRemovedReason.size());
    144                     for (JSONObject payload: mRemovedReason.values()) {
    145                         payloadData.add(payload.toString());
    146                     }
    147                     bundle.putStringArrayList(EXTRA_PAYLOAD, payloadData);
    148                     setResultExtras(bundle);
    149                     setResultCode(Activity.RESULT_OK);
    150                 } else if (SERVICE_CLEAR_ONE.equals(action)) {
    151                     String tag = intent.getStringExtra(EXTRA_TAG);
    152                     String key = mNotificationKeys.get(tag);
    153                     if (key != null) {
    154                         MockListener.this.cancelNotification(key);
    155                     } else {
    156                         Log.w(TAG, "Notification does not exist: " + tag);
    157                     }
    158                 } else if (SERVICE_CLEAR_ALL.equals(action)) {
    159                     MockListener.this.cancelAllNotifications();
    160                 } else if (SERVICE_RESET.equals(action)) {
    161                     resetData();
    162                 } else if (SERVICE_SNOOZE.equals(action)) {
    163                     MockListener.this.requestUnbind();
    164                 } else if (SERVICE_HINTS.equals(action)) {
    165                     MockListener.this.requestListenerHints(intent.getIntExtra(EXTRA_CODE, 0));
    166                 } else if (SERVICE_PROBE_HINTS.equals(action)) {
    167                     bundle.putInt(EXTRA_INT, MockListener.this.getCurrentListenerHints());
    168                     setResultExtras(bundle);
    169                     setResultCode(Activity.RESULT_OK);
    170                 } else if (SERVICE_SNOOZE_DURATION.equals(action)) {
    171                     String tag = intent.getStringExtra(EXTRA_TAG);
    172                     String key = mNotificationKeys.get(tag);
    173                     if (key != null) {
    174                         MockListener.this.snoozeNotification(key,
    175                                 intent.getLongExtra(EXTRA_LONG, (long) 0));
    176                     }
    177                 } else if (SERVICE_GET_SNOOZED.equals(action)) {
    178                     mSnoozed.clear();
    179                     StatusBarNotification[] snoozed = MockListener.this.getSnoozedNotifications();
    180                     for (StatusBarNotification sbn : snoozed) {
    181                         mSnoozed.add(sbn.getTag());
    182                     }
    183                     bundle.putStringArrayList(EXTRA_PAYLOAD, mSnoozed);
    184                     setResultExtras(bundle);
    185                     setResultCode(Activity.RESULT_OK);
    186                 } else {
    187                     Log.w(TAG, "unknown action");
    188                     setResultCode(Activity.RESULT_CANCELED);
    189                 }
    190             }
    191         };
    192         IntentFilter filter = new IntentFilter();
    193         filter.addAction(SERVICE_CHECK);
    194         filter.addAction(SERVICE_DND);
    195         filter.addAction(SERVICE_POSTED);
    196         filter.addAction(SERVICE_ORDER);
    197         filter.addAction(SERVICE_PAYLOADS);
    198         filter.addAction(SERVICE_REMOVED);
    199         filter.addAction(SERVICE_REMOVED_REASON);
    200         filter.addAction(SERVICE_CLEAR_ONE);
    201         filter.addAction(SERVICE_CLEAR_ALL);
    202         filter.addAction(SERVICE_RESET);
    203         filter.addAction(SERVICE_SNOOZE);
    204         filter.addAction(SERVICE_HINTS);
    205         filter.addAction(SERVICE_PROBE_HINTS);
    206         filter.addAction(SERVICE_SNOOZE_DURATION);
    207         filter.addAction(SERVICE_GET_SNOOZED);
    208         registerReceiver(mReceiver, filter);
    209     }
    210 
    211     @Override
    212     public void onDestroy() {
    213         super.onDestroy();
    214         unregisterReceiver(mReceiver);
    215         mReceiver = null;
    216         Log.d(TAG, "destroyed");
    217     }
    218 
    219     @Override
    220     public void onListenerConnected() {
    221         super.onListenerConnected();
    222         mDND = getCurrentInterruptionFilter();
    223         Log.d(TAG, "initial value of CurrentInterruptionFilter is " + mDND);
    224     }
    225 
    226     @Override
    227     public void onInterruptionFilterChanged(int interruptionFilter) {
    228         super.onInterruptionFilterChanged(interruptionFilter);
    229         mDND = interruptionFilter;
    230         Log.d(TAG, "value of CurrentInterruptionFilter changed to " + mDND);
    231     }
    232 
    233     public void resetData() {
    234         mPosted.clear();
    235         mNotifications.clear();
    236         mRemoved.clear();
    237         mOrder.clear();
    238         mRemovedReason.clear();
    239         mSnoozed.clear();
    240         mPostedNotifications.clear();
    241     }
    242 
    243     @Override
    244     public void onNotificationRankingUpdate(RankingMap rankingMap) {
    245         String[] orderedKeys = rankingMap.getOrderedKeys();
    246         mOrder.clear();
    247         Ranking rank = new Ranking();
    248         for( int i = 0; i < orderedKeys.length; i++) {
    249             String key = orderedKeys[i];
    250             mOrder.add(key);
    251             rankingMap.getRanking(key, rank);
    252             JSONObject note = mNotifications.get(key);
    253             if (note != null) {
    254                 try {
    255                     note.put(JSON_RANK, rank.getRank());
    256                     note.put(JSON_AMBIENT, rank.isAmbient());
    257                     note.put(JSON_MATCHES_ZEN_FILTER, rank.matchesInterruptionFilter());
    258                 } catch (JSONException e) {
    259                     Log.e(TAG, "failed to pack up notification payload", e);
    260                 }
    261             }
    262         }
    263     }
    264 
    265     @Override
    266     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
    267         if (!mTestPackages.contains(sbn.getPackageName())) { return; }
    268         Log.d(TAG, "posted: " + sbn.getTag());
    269         mPosted.add(sbn.getTag());
    270         mPostedNotifications.add(sbn.getNotification());
    271         JSONObject notification = new JSONObject();
    272         try {
    273             notification.put(JSON_TAG, sbn.getTag());
    274             notification.put(JSON_ID, sbn.getId());
    275             notification.put(JSON_PACKAGE, sbn.getPackageName());
    276             notification.put(JSON_WHEN, sbn.getNotification().when);
    277             notification.put(JSON_ICON, sbn.getNotification().icon);
    278             notification.put(JSON_FLAGS, sbn.getNotification().flags);
    279             mNotifications.put(sbn.getKey(), notification);
    280             mNotificationKeys.put(sbn.getTag(), sbn.getKey());
    281         } catch (JSONException e) {
    282             Log.e(TAG, "failed to pack up notification payload", e);
    283         }
    284         onNotificationRankingUpdate(rankingMap);
    285     }
    286 
    287     @Override
    288     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
    289         Log.d(TAG, "removed: " + sbn.getTag());
    290         mRemoved.add(sbn.getTag());
    291         mNotifications.remove(sbn.getKey());
    292         mNotificationKeys.remove(sbn.getTag());
    293         onNotificationRankingUpdate(rankingMap);
    294     }
    295 
    296     @Override
    297     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
    298             int reason) {
    299         Log.d(TAG, "removed: " + sbn.getTag());
    300         mRemoved.add(sbn.getTag());
    301         JSONObject removed = new JSONObject();
    302         try {
    303             removed.put(JSON_TAG, sbn.getTag());
    304             removed.put(JSON_REASON, reason);
    305         } catch (JSONException e) {
    306             Log.e(TAG, "failed to pack up notification payload", e);
    307         }
    308         mNotifications.remove(sbn.getKey());
    309         mNotificationKeys.remove(sbn.getTag());
    310         mRemovedReason.put(sbn.getTag(), removed);
    311         onNotificationRankingUpdate(rankingMap);
    312     }
    313 
    314     public static void resetListenerData(Context context) {
    315         sendCommand(context, SERVICE_RESET, null, 0);
    316     }
    317 
    318     public static void probeListenerStatus(Context context, StatusCatcher catcher) {
    319         requestStatus(context, SERVICE_CHECK, catcher);
    320     }
    321 
    322     public static void probeFilter(Context context, IntegerResultCatcher catcher) {
    323         requestIntegerResult(context, SERVICE_DND, catcher);
    324     }
    325 
    326     public static void probeListenerPosted(Context context, StringListResultCatcher catcher) {
    327         requestStringListResult(context, SERVICE_POSTED, catcher);
    328     }
    329 
    330     public static void probeListenerPosted(Context context, NotificationResultCatcher catcher) {
    331         requestNotificationResult(context, SERVICE_POSTED, catcher);
    332     }
    333 
    334     public static void probeListenerSnoozed(Context context, StringListResultCatcher catcher) {
    335         requestStringListResult(context, SERVICE_GET_SNOOZED, catcher);
    336     }
    337 
    338     public static void probeListenerOrder(Context context, StringListResultCatcher catcher) {
    339         requestStringListResult(context, SERVICE_ORDER, catcher);
    340     }
    341 
    342     public static void probeListenerPayloads(Context context, StringListResultCatcher catcher) {
    343         requestStringListResult(context, SERVICE_PAYLOADS, catcher);
    344     }
    345 
    346     public static void probeListenerRemoved(Context context, StringListResultCatcher catcher) {
    347         requestStringListResult(context, SERVICE_REMOVED, catcher);
    348     }
    349 
    350     public static void probeListenerRemovedWithReason(Context context,
    351             StringListResultCatcher catcher) {
    352         requestStringListResult(context, SERVICE_REMOVED_REASON, catcher);
    353     }
    354 
    355     public static void probeListenerHints(Context context, IntegerResultCatcher catcher) {
    356         requestIntegerResult(context, SERVICE_PROBE_HINTS, catcher);
    357     }
    358 
    359     public static void setHints(Context context, int hints) {
    360         Intent broadcast = new Intent(SERVICE_HINTS);
    361         broadcast.putExtra(EXTRA_CODE, hints);
    362         context.sendBroadcast(broadcast);
    363     }
    364 
    365     public static void snooze(Context context) {
    366         sendCommand(context, SERVICE_SNOOZE, null, 0);
    367     }
    368 
    369     public static void clearOne(Context context, String tag, int code) {
    370         sendCommand(context, SERVICE_CLEAR_ONE, tag, code);
    371     }
    372 
    373     public static void snoozeOneFor(Context context, String tag, long duration) {
    374         Intent broadcast = new Intent(SERVICE_SNOOZE_DURATION);
    375         if (tag != null) {
    376             broadcast.putExtra(EXTRA_TAG, tag);
    377             broadcast.putExtra(EXTRA_LONG, duration);
    378         }
    379         context.sendBroadcast(broadcast);
    380     }
    381 
    382     public static void clearAll(Context context) {
    383         sendCommand(context, SERVICE_CLEAR_ALL, null, 0);
    384     }
    385 
    386     private static void sendCommand(Context context, String action, String tag, int code) {
    387         Intent broadcast = new Intent(action);
    388         if (tag != null) {
    389             broadcast.putExtra(EXTRA_TAG, tag);
    390             broadcast.putExtra(EXTRA_CODE, code);
    391         }
    392         context.sendBroadcast(broadcast);
    393     }
    394 
    395     public abstract static class StatusCatcher extends BroadcastReceiver {
    396         @Override
    397         public void onReceive(Context context, Intent intent) {
    398             accept(Integer.valueOf(getResultCode()));
    399         }
    400 
    401         abstract public void accept(int result);
    402     }
    403 
    404     private static void requestStatus(Context context, String action,
    405             StatusCatcher catcher) {
    406         Intent broadcast = new Intent(action);
    407         context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
    408     }
    409 
    410     public abstract static class IntegerResultCatcher extends BroadcastReceiver {
    411         @Override
    412         public void onReceive(Context context, Intent intent) {
    413             accept(getResultExtras(true).getInt(EXTRA_INT, -1));
    414         }
    415 
    416         abstract public void accept(int result);
    417     }
    418 
    419     private static void requestIntegerResult(Context context, String action,
    420             IntegerResultCatcher catcher) {
    421         Intent broadcast = new Intent(action);
    422         context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
    423     }
    424 
    425     public abstract static class StringListResultCatcher extends BroadcastReceiver {
    426         @Override
    427         public void onReceive(Context context, Intent intent) {
    428             accept(getResultExtras(true).getStringArrayList(EXTRA_PAYLOAD));
    429         }
    430 
    431         abstract public void accept(List<String> result);
    432     }
    433 
    434     public abstract static class NotificationResultCatcher extends BroadcastReceiver {
    435         @Override
    436         public void onReceive(Context context, Intent intent) {
    437             ArrayList<Parcelable> parcels =
    438                     getResultExtras(true).getParcelableArrayList(EXTRA_POSTED_NOTIFICATIONS);
    439             if (parcels == null) {
    440                 parcels = new ArrayList<Parcelable>();
    441             }
    442             List<Notification> notifications = new ArrayList<Notification>(parcels.size());
    443             for (Parcelable parcel : parcels) {
    444                 if (parcel instanceof Notification) {
    445                     notifications.add((Notification) parcel);
    446                 }
    447             }
    448             accept(notifications);
    449         }
    450 
    451         abstract public void accept(List<Notification> result);
    452     }
    453 
    454     private static void requestStringListResult(Context context, String action,
    455             StringListResultCatcher catcher) {
    456         Intent broadcast = new Intent(action);
    457         context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
    458     }
    459 
    460     private static void requestNotificationResult(Context context, String action,
    461             NotificationResultCatcher catcher) {
    462         Intent broadcast = new Intent(action);
    463         context.sendOrderedBroadcast(broadcast, null, catcher, null, RESULT_NO_SERVER, null, null);
    464     }
    465 }
    466