Home | History | Annotate | Download | only in notifications
      1 /*
      2  * Copyright (C) 2014 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_AMBIENT;
     20 import static com.android.cts.verifier.notifications.MockListener.JSON_MATCHES_ZEN_FILTER;
     21 import static com.android.cts.verifier.notifications.MockListener.JSON_TAG;
     22 
     23 import android.app.ActivityManager;
     24 import android.app.Notification;
     25 import android.app.NotificationChannel;
     26 import android.app.NotificationManager;
     27 import android.content.ContentProviderOperation;
     28 import android.content.Context;
     29 import android.content.OperationApplicationException;
     30 import android.database.Cursor;
     31 import android.media.AudioAttributes;
     32 import android.net.Uri;
     33 import android.os.Build;
     34 import android.os.RemoteException;
     35 import android.provider.ContactsContract;
     36 import android.provider.ContactsContract.CommonDataKinds.Email;
     37 import android.provider.ContactsContract.CommonDataKinds.Phone;
     38 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     39 import android.util.Log;
     40 import android.view.View;
     41 import android.view.ViewGroup;
     42 
     43 import com.android.cts.verifier.R;
     44 
     45 import org.json.JSONException;
     46 import org.json.JSONObject;
     47 
     48 import java.util.ArrayList;
     49 import java.util.HashSet;
     50 import java.util.List;
     51 import java.util.Set;
     52 
     53 public class AttentionManagementVerifierActivity
     54         extends InteractiveVerifierActivity {
     55     private static final String TAG = "AttentionVerifier";
     56 
     57     private static final String NOTIFICATION_CHANNEL_ID = TAG;
     58     private static final String NOTIFICATION_CHANNEL_ID_NOISY = TAG + "/noisy";
     59     private static final String NOTIFICATION_CHANNEL_ID_MEDIA = TAG + "/media";
     60     private static final String NOTIFICATION_CHANNEL_ID_GAME = TAG + "/game";
     61     private static final String ALICE = "Alice";
     62     private static final String ALICE_PHONE = "+16175551212";
     63     private static final String ALICE_EMAIL = "alice (at) _foo._bar";
     64     private static final String BOB = "Bob";
     65     private static final String BOB_PHONE = "+16505551212";;
     66     private static final String BOB_EMAIL = "bob (at) _foo._bar";
     67     private static final String CHARLIE = "Charlie";
     68     private static final String CHARLIE_PHONE = "+13305551212";
     69     private static final String CHARLIE_EMAIL = "charlie (at) _foo._bar";
     70     private static final int MODE_NONE = 0;
     71     private static final int MODE_URI = 1;
     72     private static final int MODE_PHONE = 2;
     73     private static final int MODE_EMAIL = 3;
     74     private static final int SEND_A = 0x1;
     75     private static final int SEND_B = 0x2;
     76     private static final int SEND_C = 0x4;
     77     private static final int SEND_ALL = SEND_A | SEND_B | SEND_C;
     78 
     79     private Uri mAliceUri;
     80     private Uri mBobUri;
     81     private Uri mCharlieUri;
     82 
     83     @Override
     84     protected int getTitleResource() {
     85         return R.string.attention_test;
     86     }
     87 
     88     @Override
     89     protected int getInstructionsResource() {
     90         return R.string.attention_info;
     91     }
     92 
     93     // Test Setup
     94 
     95     @Override
     96     protected List<InteractiveTestCase> createTestItems() {
     97 
     98         List<InteractiveTestCase> tests = new ArrayList<>(17);
     99         ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    100         if (am.isLowRamDevice()) {
    101             tests.add(new CannotBeEnabledTest());
    102             tests.add(new ServiceStoppedTest());
    103         } else {
    104             tests.add(new IsEnabledTest());
    105             tests.add(new ServiceStartedTest());
    106             tests.add(new InsertContactsTest());
    107             tests.add(new NoneInterceptsAllMessagesTest());
    108             tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
    109             tests.add(new PriorityInterceptsSomeMessagesTest());
    110 
    111             if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
    112                 // Tests targeting P and above:
    113                 tests.add(new PriorityInterceptsAlarmsTest());
    114                 tests.add(new PriorityInterceptsMediaSystemOtherTest());
    115             }
    116 
    117             tests.add(new AllInterceptsNothingMessagesTest());
    118             tests.add(new AllInterceptsNothingDiffCategoriesTest());
    119             tests.add(new DefaultOrderTest());
    120             tests.add(new PriorityOrderTest());
    121             tests.add(new InterruptionOrderTest());
    122             tests.add(new AmbientBitsTest());
    123             tests.add(new LookupUriOrderTest());
    124             tests.add(new EmailOrderTest());
    125             tests.add(new PhoneOrderTest());
    126             tests.add(new DeleteContactsTest());
    127         }
    128         return tests;
    129     }
    130 
    131     private void createChannels() {
    132         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
    133                 NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_MIN);
    134         mNm.createNotificationChannel(channel);
    135         NotificationChannel noisyChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY,
    136                 NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
    137         noisyChannel.enableVibration(true);
    138         mNm.createNotificationChannel(noisyChannel);
    139         NotificationChannel mediaChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA,
    140                 NOTIFICATION_CHANNEL_ID_MEDIA, NotificationManager.IMPORTANCE_HIGH);
    141         AudioAttributes.Builder aa = new AudioAttributes.Builder()
    142                 .setUsage(AudioAttributes.USAGE_MEDIA);
    143         mediaChannel.setSound(null, aa.build());
    144         mNm.createNotificationChannel(mediaChannel);
    145         NotificationChannel gameChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_GAME,
    146                 NOTIFICATION_CHANNEL_ID_GAME, NotificationManager.IMPORTANCE_HIGH);
    147         AudioAttributes.Builder aa2 = new AudioAttributes.Builder()
    148                 .setUsage(AudioAttributes.USAGE_GAME);
    149         gameChannel.setSound(null, aa2.build());
    150         mNm.createNotificationChannel(gameChannel);
    151     }
    152 
    153     private void deleteChannels() {
    154         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
    155         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY);
    156         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA);
    157         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_GAME);
    158     }
    159 
    160     // Tests
    161 
    162     private class ServiceStoppedTest extends InteractiveTestCase {
    163         int mRetries = 3;
    164         @Override
    165         protected View inflate(ViewGroup parent) {
    166             return createAutoItem(parent, R.string.nls_service_stopped);
    167         }
    168 
    169         @Override
    170         protected void test() {
    171             if (MockListener.getInstance() == null
    172                     || !MockListener.getInstance().isConnected) {
    173                 status = PASS;
    174             } else {
    175                 if (--mRetries > 0) {
    176                     sleep(100);
    177                     status = RETEST;
    178                 } else {
    179                     status = FAIL;
    180                 }
    181             }
    182         }
    183     }
    184 
    185     protected class InsertContactsTest extends InteractiveTestCase {
    186         @Override
    187         protected View inflate(ViewGroup parent) {
    188             return createAutoItem(parent, R.string.attention_create_contacts);
    189         }
    190 
    191         @Override
    192         protected void setUp() {
    193             insertSingleContact(ALICE, ALICE_PHONE, ALICE_EMAIL, true);
    194             insertSingleContact(BOB, BOB_PHONE, BOB_EMAIL, false);
    195             // charlie is not in contacts
    196             status = READY;
    197         }
    198 
    199         @Override
    200         protected void test() {
    201             mAliceUri = lookupContact(ALICE_PHONE);
    202             mBobUri = lookupContact(BOB_PHONE);
    203             mCharlieUri = lookupContact(CHARLIE_PHONE);
    204 
    205             status = PASS;
    206             if (mAliceUri == null) { status = FAIL; }
    207             if (mBobUri == null) { status = FAIL; }
    208             if (mCharlieUri != null) { status = FAIL; }
    209 
    210             if (status == PASS && !isStarred(mAliceUri)) {
    211                 status = RETEST;
    212                 Log.i("InsertContactsTest", "Alice is not yet starred");
    213             } else {
    214                 Log.i("InsertContactsTest", "Alice is: " + mAliceUri);
    215                 Log.i("InsertContactsTest", "Bob is: " + mBobUri);
    216                 Log.i("InsertContactsTest", "Charlie is: " + mCharlieUri);
    217                 next();
    218             }
    219         }
    220     }
    221 
    222     protected class DeleteContactsTest extends InteractiveTestCase {
    223         @Override
    224         protected View inflate(ViewGroup parent) {
    225             return createAutoItem(parent, R.string.attention_delete_contacts);
    226         }
    227 
    228         @Override
    229         protected void test() {
    230             final ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
    231             operationList.add(ContentProviderOperation.newDelete(mAliceUri).build());
    232             operationList.add(ContentProviderOperation.newDelete(mBobUri).build());
    233             try {
    234                 mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
    235                 status = READY;
    236             } catch (RemoteException e) {
    237                 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
    238                 status = FAIL;
    239             } catch (OperationApplicationException e) {
    240                 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
    241                 status = FAIL;
    242             }
    243             status = PASS;
    244             next();
    245         }
    246     }
    247 
    248     protected class NoneInterceptsAllMessagesTest extends InteractiveTestCase {
    249         @Override
    250         protected View inflate(ViewGroup parent) {
    251             return createAutoItem(parent, R.string.attention_all_are_filtered);
    252         }
    253 
    254         @Override
    255         protected void setUp() {
    256             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
    257             createChannels();
    258             sendNotifications(MODE_URI, false, false);
    259             status = READY;
    260         }
    261 
    262         @Override
    263         protected void test() {
    264             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    265 
    266             Set<String> found = new HashSet<String>();
    267             if (result.size() == 0) {
    268                 status = FAIL;
    269                 return;
    270             }
    271             boolean pass = true;
    272             for (JSONObject payload : result) {
    273                 try {
    274                     String tag = payload.getString(JSON_TAG);
    275                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    276                     Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
    277                     if (found.contains(tag)) {
    278                         // multiple entries for same notification!
    279                         pass = false;
    280                     } else if (ALICE.equals(tag)) {
    281                         found.add(ALICE);
    282                         pass &= !zen;
    283                     } else if (BOB.equals(tag)) {
    284                         found.add(BOB);
    285                         pass &= !zen;
    286                     } else if (CHARLIE.equals(tag)) {
    287                         found.add(CHARLIE);
    288                         pass &= !zen;
    289                     }
    290                 } catch (JSONException e) {
    291                     pass = false;
    292                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    293                 }
    294             }
    295             pass &= found.size() == 3;
    296             status = pass ? PASS : FAIL;
    297         }
    298 
    299         @Override
    300         protected void tearDown() {
    301             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    302             mNm.cancelAll();
    303             deleteChannels();
    304             MockListener.getInstance().resetData();
    305         }
    306     }
    307 
    308     protected class NoneInterceptsAlarmEventReminderCategoriesTest extends InteractiveTestCase {
    309         @Override
    310         protected View inflate(ViewGroup parent) {
    311             return createAutoItem(parent, R.string.attention_all_are_filtered);
    312         }
    313 
    314         @Override
    315         protected void setUp() {
    316             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
    317             createChannels();
    318             sendEventAlarmReminderNotifications(SEND_ALL);
    319             status = READY;
    320         }
    321 
    322         @Override
    323         protected void test() {
    324             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    325 
    326             Set<String> found = new HashSet<String>();
    327             if (result.size() == 0) {
    328                 status = FAIL;
    329                 return;
    330             }
    331             boolean pass = true;
    332             for (JSONObject payload : result) {
    333                 try {
    334                     String tag = payload.getString(JSON_TAG);
    335                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    336                     Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
    337                     if (found.contains(tag)) {
    338                         // multiple entries for same notification!
    339                         pass = false;
    340                     } else if (ALICE.equals(tag)) {
    341                         found.add(ALICE);
    342                         pass &= !zen;
    343                     } else if (BOB.equals(tag)) {
    344                         found.add(BOB);
    345                         pass &= !zen;
    346                     } else if (CHARLIE.equals(tag)) {
    347                         found.add(CHARLIE);
    348                         pass &= !zen;
    349                     }
    350                 } catch (JSONException e) {
    351                     pass = false;
    352                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    353                 }
    354             }
    355             pass &= found.size() == 3;
    356             status = pass ? PASS : FAIL;
    357         }
    358 
    359         @Override
    360         protected void tearDown() {
    361             mNm.cancelAll();
    362             deleteChannels();
    363             MockListener.getInstance().resetData();
    364         }
    365     }
    366 
    367     protected class AllInterceptsNothingMessagesTest extends InteractiveTestCase {
    368         @Override
    369         protected View inflate(ViewGroup parent) {
    370             return createAutoItem(parent, R.string.attention_none_are_filtered_messages);
    371         }
    372 
    373         @Override
    374         protected void setUp() {
    375             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    376             createChannels();
    377             sendNotifications(MODE_URI, false, false); // different messages
    378             status = READY;
    379         }
    380 
    381         @Override
    382         protected void test() {
    383             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    384 
    385             Set<String> found = new HashSet<String>();
    386             if (result.size() == 0) {
    387                 status = FAIL;
    388                 return;
    389             }
    390             boolean pass = true;
    391             for (JSONObject payload : result) {
    392                 try {
    393                     String tag = payload.getString(JSON_TAG);
    394                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    395                     Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
    396                     if (found.contains(tag)) {
    397                         // multiple entries for same notification!
    398                         pass = false;
    399                     } else if (ALICE.equals(tag)) {
    400                         found.add(ALICE);
    401                         pass &= zen;
    402                     } else if (BOB.equals(tag)) {
    403                         found.add(BOB);
    404                         pass &= zen;
    405                     } else if (CHARLIE.equals(tag)) {
    406                         found.add(CHARLIE);
    407                         pass &= zen;
    408                     }
    409                 } catch (JSONException e) {
    410                     pass = false;
    411                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    412                 }
    413             }
    414             pass &= found.size() == 3;
    415             status = pass ? PASS : FAIL;
    416         }
    417 
    418         @Override
    419         protected void tearDown() {
    420             mNm.cancelAll();
    421             deleteChannels();
    422             MockListener.getInstance().resetData();
    423         }
    424     }
    425 
    426     protected class AllInterceptsNothingDiffCategoriesTest extends InteractiveTestCase {
    427         @Override
    428         protected View inflate(ViewGroup parent) {
    429             return createAutoItem(parent, R.string.attention_none_are_filtered_diff_categories);
    430         }
    431 
    432         @Override
    433         protected void setUp() {
    434             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    435             createChannels();
    436             sendEventAlarmReminderNotifications(SEND_ALL);
    437             status = READY;
    438         }
    439 
    440         @Override
    441         protected void test() {
    442             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    443 
    444             Set<String> found = new HashSet<String>();
    445             if (result.size() == 0) {
    446                 status = FAIL;
    447                 return;
    448             }
    449             boolean pass = true;
    450             for (JSONObject payload : result) {
    451                 try {
    452                     String tag = payload.getString(JSON_TAG);
    453                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    454                     Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
    455                     if (found.contains(tag)) {
    456                         // multiple entries for same notification!
    457                         pass = false;
    458                     } else if (ALICE.equals(tag)) {
    459                         found.add(ALICE);
    460                         pass &= zen;
    461                     } else if (BOB.equals(tag)) {
    462                         found.add(BOB);
    463                         pass &= zen;
    464                     } else if (CHARLIE.equals(tag)) {
    465                         found.add(CHARLIE);
    466                         pass &= zen;
    467                     }
    468                 } catch (JSONException e) {
    469                     pass = false;
    470                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    471                 }
    472             }
    473             pass &= found.size() == 3;
    474             status = pass ? PASS : FAIL;
    475         }
    476 
    477         @Override
    478         protected void tearDown() {
    479             mNm.cancelAll();
    480             deleteChannels();
    481             MockListener.getInstance().resetData();
    482         }
    483     }
    484 
    485     protected class PriorityInterceptsSomeMessagesTest extends InteractiveTestCase {
    486         @Override
    487         protected View inflate(ViewGroup parent) {
    488             return createAutoItem(parent, R.string.attention_some_are_filtered_messages);
    489         }
    490 
    491         @Override
    492         protected void setUp() {
    493             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
    494             NotificationManager.Policy policy = mNm.getNotificationPolicy();
    495             policy = new NotificationManager.Policy(
    496                     NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
    497                     policy.priorityCallSenders,
    498                     NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
    499             mNm.setNotificationPolicy(policy);
    500             createChannels();
    501             sendNotifications(MODE_URI, false, false);
    502             status = READY;
    503         }
    504 
    505         @Override
    506         protected void test() {
    507             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    508 
    509             Set<String> found = new HashSet<String>();
    510             if (result.size() == 0) {
    511                 status = FAIL;
    512                 return;
    513             }
    514             boolean pass = true;
    515             for (JSONObject payload : result) {
    516                 try {
    517                     String tag = payload.getString(JSON_TAG);
    518                     boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    519                     Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
    520                     if (found.contains(tag)) {
    521                         // multiple entries for same notification!
    522                         pass = false;
    523                     } else if (ALICE.equals(tag)) {
    524                         found.add(ALICE);
    525                         pass &= zen;
    526                     } else if (BOB.equals(tag)) {
    527                         found.add(BOB);
    528                         pass &= !zen;
    529                     } else if (CHARLIE.equals(tag)) {
    530                         found.add(CHARLIE);
    531                         pass &= !zen;
    532                     }
    533                 } catch (JSONException e) {
    534                     pass = false;
    535                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    536                 }
    537             }
    538             pass &= found.size() >= 3;
    539             status = pass ? PASS : FAIL;
    540         }
    541 
    542         @Override
    543         protected void tearDown() {
    544             mNm.cancelAll();
    545             deleteChannels();
    546             MockListener.getInstance().resetData();
    547         }
    548     }
    549 
    550     protected class PriorityInterceptsAlarmsTest extends InteractiveTestCase {
    551         @Override
    552         protected View inflate(ViewGroup parent) {
    553             return createAutoItem(parent, R.string.attention_some_are_filtered_alarms);
    554         }
    555 
    556         @Override
    557         protected void setUp() {
    558             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
    559             NotificationManager.Policy policy = mNm.getNotificationPolicy();
    560             policy = new NotificationManager.Policy(
    561                     NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
    562                     policy.priorityCallSenders,
    563                     policy.priorityMessageSenders);
    564             mNm.setNotificationPolicy(policy);
    565             createChannels();
    566             // Event to Alice, Alarm to Bob, Reminder to Charlie:
    567             sendEventAlarmReminderNotifications(SEND_ALL);
    568             status = READY;
    569         }
    570 
    571         @Override
    572         protected void test() {
    573             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    574 
    575             Set<String> found = new HashSet<String>();
    576             if (result.size() == 0) {
    577                 status = FAIL;
    578                 return;
    579             }
    580             boolean pass = true;
    581             for (JSONObject payload : result) {
    582                 try {
    583                     String tag = payload.getString(JSON_TAG);
    584                     boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    585                     Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
    586                     if (found.contains(tag)) {
    587                         // multiple entries for same notification!
    588                         pass = false;
    589                     } else if (ALICE.equals(tag)) {
    590                         found.add(ALICE);
    591                         pass &= zenIntercepted; // Alice's event notif should be intercepted
    592                     } else if (BOB.equals(tag)) {
    593                         found.add(BOB);
    594                         pass &= !zenIntercepted;   // Bob's alarm notif should not be intercepted
    595                     } else if (CHARLIE.equals(tag)) {
    596                         found.add(CHARLIE);
    597                         pass &= zenIntercepted; // Charlie's reminder notif should be intercepted
    598                     }
    599                 } catch (JSONException e) {
    600                     pass = false;
    601                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    602                 }
    603             }
    604             pass &= found.size() >= 3;
    605             status = pass ? PASS : FAIL;
    606         }
    607 
    608         @Override
    609         protected void tearDown() {
    610             mNm.cancelAll();
    611             deleteChannels();
    612             MockListener.getInstance().resetData();
    613         }
    614     }
    615 
    616     protected class PriorityInterceptsMediaSystemOtherTest extends InteractiveTestCase {
    617         @Override
    618         protected View inflate(ViewGroup parent) {
    619             return createAutoItem(parent, R.string.attention_some_are_filtered_media_system_other);
    620         }
    621 
    622         @Override
    623         protected void setUp() {
    624             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
    625             NotificationManager.Policy policy = mNm.getNotificationPolicy();
    626             policy = new NotificationManager.Policy(
    627                     NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA,
    628                     policy.priorityCallSenders,
    629                     policy.priorityMessageSenders);
    630             mNm.setNotificationPolicy(policy);
    631             createChannels();
    632             // Alarm to Alice, Other (Game) to Bob, Media to Charlie:
    633             sendAlarmOtherMediaNotifications(SEND_ALL);
    634             status = READY;
    635         }
    636 
    637         @Override
    638         protected void test() {
    639             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    640 
    641             Set<String> found = new HashSet<String>();
    642             if (result.size() == 0) {
    643                 status = FAIL;
    644                 return;
    645             }
    646             boolean pass = true;
    647             for (JSONObject payload : result) {
    648                 try {
    649                     String tag = payload.getString(JSON_TAG);
    650                     boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
    651                     Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
    652                     if (found.contains(tag)) {
    653                         // multiple entries for same notification!
    654                         pass = false;
    655                     } else if (ALICE.equals(tag)) {
    656                         found.add(ALICE);
    657                         pass &= zenIntercepted;
    658                     } else if (BOB.equals(tag)) {
    659                         found.add(BOB);
    660                         pass &= !zenIntercepted;
    661                     } else if (CHARLIE.equals(tag)) {
    662                         found.add(CHARLIE);
    663                         pass &= !zenIntercepted;
    664                     }
    665                 } catch (JSONException e) {
    666                     pass = false;
    667                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    668                 }
    669             }
    670             pass &= found.size() >= 3;
    671             status = pass ? PASS : FAIL;
    672         }
    673 
    674         @Override
    675         protected void tearDown() {
    676             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    677             mNm.cancelAll();
    678             deleteChannels();
    679             MockListener.getInstance().resetData();
    680         }
    681     }
    682 
    683     // ordered by time: C, B, A
    684     protected class DefaultOrderTest extends InteractiveTestCase {
    685         @Override
    686         protected View inflate(ViewGroup parent) {
    687             return createAutoItem(parent, R.string.attention_default_order);
    688         }
    689 
    690         @Override
    691         protected void setUp() {
    692             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    693             createChannels();
    694             sendNotifications(MODE_NONE, false, false);
    695             status = READY;
    696         }
    697 
    698         @Override
    699         protected void test() {
    700             List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    701             int rankA = findTagInKeys(ALICE, orderedKeys);
    702             int rankB = findTagInKeys(BOB, orderedKeys);
    703             int rankC = findTagInKeys(CHARLIE, orderedKeys);
    704             if (rankC < rankB && rankB < rankA) {
    705                 status = PASS;
    706             } else {
    707                 logFail(rankA + ", " + rankB + ", " + rankC);
    708                 status = FAIL;
    709             }
    710         }
    711 
    712         @Override
    713         protected void tearDown() {
    714             mNm.cancelAll();
    715             deleteChannels();
    716             MockListener.getInstance().resetData();
    717         }
    718     }
    719 
    720     // ordered by priority: B, C, A
    721     protected class PriorityOrderTest extends InteractiveTestCase {
    722         @Override
    723         protected View inflate(ViewGroup parent) {
    724             return createAutoItem(parent, R.string.attention_priority_order);
    725         }
    726 
    727         @Override
    728         protected void setUp() {
    729             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    730             createChannels();
    731             sendNotifications(MODE_NONE, true, false);
    732             status = READY;
    733         }
    734 
    735         @Override
    736         protected void test() {
    737             List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    738             int rankA = findTagInKeys(ALICE, orderedKeys);
    739             int rankB = findTagInKeys(BOB, orderedKeys);
    740             int rankC = findTagInKeys(CHARLIE, orderedKeys);
    741             if (rankB < rankC && rankC < rankA) {
    742                 status = PASS;
    743             } else {
    744                 logFail(rankA + ", " + rankB + ", " + rankC);
    745                 status = FAIL;
    746             }
    747         }
    748 
    749         @Override
    750         protected void tearDown() {
    751             mNm.cancelAll();
    752             deleteChannels();
    753             MockListener.getInstance().resetData();
    754         }
    755     }
    756 
    757     // A starts at the top then falls to the bottom
    758     protected class InterruptionOrderTest extends InteractiveTestCase {
    759         boolean mSawElevation = false;
    760 
    761         @Override
    762         protected View inflate(ViewGroup parent) {
    763             return createAutoItem(parent, R.string.attention_interruption_order);
    764         }
    765 
    766         @Override
    767         protected void setUp() {
    768             mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
    769             delayTime = 15000;
    770             createChannels();
    771             // send B & C noisy with contact affinity
    772             sendNotifications(SEND_B, MODE_URI, false, true);
    773             sleep(1000);
    774             sendNotifications(SEND_C, MODE_URI, false, true);
    775             status = READY_AFTER_LONG_DELAY;
    776         }
    777 
    778         @Override
    779         protected void test() {
    780             if (status == READY_AFTER_LONG_DELAY) {
    781                 // send A noisy but no contact affinity
    782                 sendNotifications(SEND_A, MODE_NONE, false, true);
    783                 status = RETEST;
    784             } else if (status == RETEST || status == RETEST_AFTER_LONG_DELAY) {
    785                 List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    786                 int rankA = findTagInKeys(ALICE, orderedKeys);
    787                 int rankB = findTagInKeys(BOB, orderedKeys);
    788                 int rankC = findTagInKeys(CHARLIE, orderedKeys);
    789                 if (!mSawElevation) {
    790                     if (rankA < rankB && rankA < rankC) {
    791                         mSawElevation = true;
    792                         status = RETEST_AFTER_LONG_DELAY;
    793                     } else {
    794                         logFail("noisy notification did not sort to top.");
    795                         status = FAIL;
    796                     }
    797                 } else {
    798                     if (rankA > rankB && rankA > rankC) {
    799                         status = PASS;
    800                     } else {
    801                         logFail("noisy notification did not fade back into the list.");
    802                         status = FAIL;
    803                     }
    804                 }
    805             }
    806         }
    807 
    808         @Override
    809         protected void tearDown() {
    810             mNm.cancelAll();
    811             deleteChannels();
    812             MockListener.getInstance().resetData();
    813         }
    814     }
    815 
    816     // B & C above the fold, A below
    817     protected class AmbientBitsTest extends InteractiveTestCase {
    818         @Override
    819         protected View inflate(ViewGroup parent) {
    820             return createAutoItem(parent, R.string.attention_ambient_bit);
    821         }
    822 
    823         @Override
    824         protected void setUp() {
    825             createChannels();
    826             sendNotifications(SEND_B | SEND_C, MODE_NONE, true, true);
    827             sendNotifications(SEND_A, MODE_NONE, true, false);
    828             status = READY;
    829         }
    830 
    831         @Override
    832         protected void test() {
    833             List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
    834 
    835             Set<String> found = new HashSet<String>();
    836             if (result.size() == 0) {
    837                 status = FAIL;
    838                 return;
    839             }
    840             boolean pass = true;
    841             for (JSONObject payload : result) {
    842                 try {
    843                     String tag = payload.getString(JSON_TAG);
    844                     boolean ambient = payload.getBoolean(JSON_AMBIENT);
    845                     Log.e(TAG, tag + (ambient ? " is" : " isn't") + " ambient");
    846                     if (found.contains(tag)) {
    847                         // multiple entries for same notification!
    848                         pass = false;
    849                     } else if (ALICE.equals(tag)) {
    850                         found.add(ALICE);
    851                         pass &= ambient;
    852                     } else if (BOB.equals(tag)) {
    853                         found.add(BOB);
    854                         pass &= !ambient;
    855                     } else if (CHARLIE.equals(tag)) {
    856                         found.add(CHARLIE);
    857                         pass &= !ambient;
    858                     }
    859                 } catch (JSONException e) {
    860                     pass = false;
    861                     Log.e(TAG, "failed to unpack data from mocklistener", e);
    862                 }
    863             }
    864             pass &= found.size() == 3;
    865             status = pass ? PASS : FAIL;
    866         }
    867 
    868         @Override
    869         protected void tearDown() {
    870             mNm.cancelAll();
    871             deleteChannels();
    872             MockListener.getInstance().resetData();
    873         }
    874     }
    875 
    876     // ordered by contact affinity: A, B, C
    877     protected class LookupUriOrderTest extends InteractiveTestCase {
    878         @Override
    879         protected View inflate(ViewGroup parent) {
    880             return createAutoItem(parent, R.string.attention_lookup_order);
    881         }
    882 
    883         @Override
    884         protected void setUp() {
    885             createChannels();
    886             sendNotifications(MODE_URI, false, false);
    887             status = READY;
    888         }
    889 
    890         @Override
    891         protected void test() {
    892             List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    893             int rankA = findTagInKeys(ALICE, orderedKeys);
    894             int rankB = findTagInKeys(BOB, orderedKeys);
    895             int rankC = findTagInKeys(CHARLIE, orderedKeys);
    896             if (rankA < rankB && rankB < rankC) {
    897                 status = PASS;
    898             } else {
    899                 logFail(rankA + ", " + rankB + ", " + rankC);
    900                 status = FAIL;
    901             }
    902         }
    903 
    904         @Override
    905         protected void tearDown() {
    906             mNm.cancelAll();
    907             deleteChannels();
    908             MockListener.getInstance().resetData();
    909         }
    910     }
    911 
    912     // ordered by contact affinity: A, B, C
    913     protected class EmailOrderTest extends InteractiveTestCase {
    914         @Override
    915         protected View inflate(ViewGroup parent) {
    916             return createAutoItem(parent, R.string.attention_email_order);
    917         }
    918 
    919         @Override
    920         protected void setUp() {
    921             createChannels();
    922             sendNotifications(MODE_EMAIL, false, false);
    923             status = READY;
    924         }
    925 
    926         @Override
    927         protected void test() {
    928             List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    929             int rankA = findTagInKeys(ALICE, orderedKeys);
    930             int rankB = findTagInKeys(BOB, orderedKeys);
    931             int rankC = findTagInKeys(CHARLIE, orderedKeys);
    932             if (rankA < rankB && rankB < rankC) {
    933                 status = PASS;
    934             } else {
    935                 logFail(rankA + ", " + rankB + ", " + rankC);
    936                 status = FAIL;
    937             }
    938         }
    939 
    940         @Override
    941         protected void tearDown() {
    942             mNm.cancelAll();
    943             deleteChannels();
    944             MockListener.getInstance().resetData();
    945         }
    946     }
    947 
    948     // ordered by contact affinity: A, B, C
    949     protected class PhoneOrderTest extends InteractiveTestCase {
    950         @Override
    951         protected View inflate(ViewGroup parent) {
    952             return createAutoItem(parent, R.string.attention_phone_order);
    953         }
    954 
    955         @Override
    956         protected void setUp() {
    957             createChannels();
    958             sendNotifications(MODE_PHONE, false, false);
    959             status = READY;
    960         }
    961 
    962         @Override
    963         protected void test() {
    964             List<String> orderedKeys = new ArrayList<>(MockListener.getInstance().mOrder);
    965             int rankA = findTagInKeys(ALICE, orderedKeys);
    966             int rankB = findTagInKeys(BOB, orderedKeys);
    967             int rankC = findTagInKeys(CHARLIE, orderedKeys);
    968             if (rankA < rankB && rankB < rankC) {
    969                 status = PASS;
    970             } else {
    971                 logFail(rankA + ", " + rankB + ", " + rankC);
    972                 status = FAIL;
    973             }
    974         }
    975 
    976         @Override
    977         protected void tearDown() {
    978             mNm.cancelAll();
    979             deleteChannels();
    980             MockListener.getInstance().resetData();
    981         }
    982     }
    983 
    984     // Utilities
    985 
    986     // usePriorities true: B, C, A
    987     // usePriorities false:
    988     //   MODE_NONE: C, B, A
    989     //   otherwise: A, B ,C
    990     private void sendNotifications(int annotationMode, boolean uriMode, boolean noisy) {
    991         sendNotifications(SEND_ALL, annotationMode, uriMode, noisy);
    992     }
    993 
    994     private void sendNotifications(int which, int uriMode, boolean usePriorities, boolean noisy) {
    995         // C, B, A when sorted by time.  Times must be in the past
    996         long whenA = System.currentTimeMillis() - 4000000L;
    997         long whenB = System.currentTimeMillis() - 2000000L;
    998         long whenC = System.currentTimeMillis() - 1000000L;
    999 
   1000         // B, C, A when sorted by priorities
   1001         int priorityA = usePriorities ? Notification.PRIORITY_MIN : Notification.PRIORITY_DEFAULT;
   1002         int priorityB = usePriorities ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
   1003         int priorityC = usePriorities ? Notification.PRIORITY_LOW : Notification.PRIORITY_DEFAULT;
   1004 
   1005         final String channelId = noisy ? NOTIFICATION_CHANNEL_ID_NOISY : NOTIFICATION_CHANNEL_ID;
   1006 
   1007         if ((which & SEND_B) != 0) {
   1008             Notification.Builder bob = new Notification.Builder(mContext, channelId)
   1009                     .setContentTitle(BOB)
   1010                     .setContentText(BOB)
   1011                     .setSmallIcon(R.drawable.ic_stat_bob)
   1012                     .setPriority(priorityB)
   1013                     .setCategory(Notification.CATEGORY_MESSAGE)
   1014                     .setWhen(whenB);
   1015             addPerson(uriMode, bob, mBobUri, BOB_PHONE, BOB_EMAIL);
   1016             mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
   1017         }
   1018         if ((which & SEND_C) != 0) {
   1019             Notification.Builder charlie =
   1020                     new Notification.Builder(mContext, channelId)
   1021                             .setContentTitle(CHARLIE)
   1022                             .setContentText(CHARLIE)
   1023                             .setSmallIcon(R.drawable.ic_stat_charlie)
   1024                             .setPriority(priorityC)
   1025                             .setCategory(Notification.CATEGORY_MESSAGE)
   1026                             .setWhen(whenC);
   1027             addPerson(uriMode, charlie, mCharlieUri, CHARLIE_PHONE, CHARLIE_EMAIL);
   1028             mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
   1029         }
   1030         if ((which & SEND_A) != 0) {
   1031             Notification.Builder alice = new Notification.Builder(mContext, channelId)
   1032                     .setContentTitle(ALICE)
   1033                     .setContentText(ALICE)
   1034                     .setSmallIcon(R.drawable.ic_stat_alice)
   1035                     .setPriority(priorityA)
   1036                     .setCategory(Notification.CATEGORY_MESSAGE)
   1037                     .setWhen(whenA);
   1038             addPerson(uriMode, alice, mAliceUri, ALICE_PHONE, ALICE_EMAIL);
   1039             mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
   1040         }
   1041     }
   1042 
   1043     private void sendEventAlarmReminderNotifications(int which) {
   1044         long when = System.currentTimeMillis() - 4000000L;
   1045         final String channelId = NOTIFICATION_CHANNEL_ID;
   1046 
   1047         // Event notification to Alice
   1048         if ((which & SEND_A) != 0) {
   1049             Notification.Builder alice = new Notification.Builder(mContext, channelId)
   1050                     .setContentTitle(ALICE)
   1051                     .setContentText(ALICE)
   1052                     .setSmallIcon(R.drawable.ic_stat_alice)
   1053                     .setCategory(Notification.CATEGORY_EVENT)
   1054                     .setWhen(when);
   1055             mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
   1056         }
   1057 
   1058         // Alarm notification to Bob
   1059         if ((which & SEND_B) != 0) {
   1060             Notification.Builder bob = new Notification.Builder(mContext, channelId)
   1061                     .setContentTitle(BOB)
   1062                     .setContentText(BOB)
   1063                     .setSmallIcon(R.drawable.ic_stat_bob)
   1064                     .setCategory(Notification.CATEGORY_ALARM)
   1065                     .setWhen(when);
   1066             mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
   1067         }
   1068 
   1069         // Reminder notification to Charlie
   1070         if ((which & SEND_C) != 0) {
   1071             Notification.Builder charlie =
   1072                     new Notification.Builder(mContext, channelId)
   1073                             .setContentTitle(CHARLIE)
   1074                             .setContentText(CHARLIE)
   1075                             .setSmallIcon(R.drawable.ic_stat_charlie)
   1076                             .setCategory(Notification.CATEGORY_REMINDER)
   1077                             .setWhen(when);
   1078             mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
   1079         }
   1080     }
   1081 
   1082     private void sendAlarmOtherMediaNotifications(int which) {
   1083         long when = System.currentTimeMillis() - 4000000L;
   1084         final String channelId = NOTIFICATION_CHANNEL_ID;
   1085 
   1086         // Alarm notification to Alice
   1087         if ((which & SEND_A) != 0) {
   1088             Notification.Builder alice = new Notification.Builder(mContext, channelId)
   1089                     .setContentTitle(ALICE)
   1090                     .setContentText(ALICE)
   1091                     .setSmallIcon(R.drawable.ic_stat_alice)
   1092                     .setCategory(Notification.CATEGORY_ALARM)
   1093                     .setWhen(when);
   1094             mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
   1095         }
   1096 
   1097         // "Other" notification to Bob
   1098         if ((which & SEND_B) != 0) {
   1099             Notification.Builder bob = new Notification.Builder(mContext,
   1100                     NOTIFICATION_CHANNEL_ID_GAME)
   1101                     .setContentTitle(BOB)
   1102                     .setContentText(BOB)
   1103                     .setSmallIcon(R.drawable.ic_stat_bob)
   1104                     .setWhen(when);
   1105             mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
   1106         }
   1107 
   1108         // Media notification to Charlie
   1109         if ((which & SEND_C) != 0) {
   1110             Notification.Builder charlie =
   1111                     new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID_MEDIA)
   1112                             .setContentTitle(CHARLIE)
   1113                             .setContentText(CHARLIE)
   1114                             .setSmallIcon(R.drawable.ic_stat_charlie)
   1115                             .setWhen(when);
   1116             mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
   1117         }
   1118     }
   1119 
   1120     private void addPerson(int mode, Notification.Builder note,
   1121             Uri uri, String phone, String email) {
   1122         if (mode == MODE_URI && uri != null) {
   1123             note.addPerson(uri.toString());
   1124         } else if (mode == MODE_PHONE) {
   1125             note.addPerson(Uri.fromParts("tel", phone, null).toString());
   1126         } else if (mode == MODE_EMAIL) {
   1127             note.addPerson(Uri.fromParts("mailto", email, null).toString());
   1128         }
   1129     }
   1130 
   1131     private void insertSingleContact(String name, String phone, String email, boolean starred) {
   1132         final ArrayList<ContentProviderOperation> operationList =
   1133                 new ArrayList<ContentProviderOperation>();
   1134         ContentProviderOperation.Builder builder =
   1135                 ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
   1136         builder.withValue(ContactsContract.RawContacts.STARRED, starred ? 1 : 0);
   1137         operationList.add(builder.build());
   1138 
   1139         builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
   1140         builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
   1141         builder.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
   1142         builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
   1143         operationList.add(builder.build());
   1144 
   1145         if (phone != null) {
   1146             builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
   1147             builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
   1148             builder.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1149             builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
   1150             builder.withValue(Phone.NUMBER, phone);
   1151             builder.withValue(ContactsContract.Data.IS_PRIMARY, 1);
   1152             operationList.add(builder.build());
   1153         }
   1154         if (email != null) {
   1155             builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
   1156             builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
   1157             builder.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1158             builder.withValue(Email.TYPE, Email.TYPE_HOME);
   1159             builder.withValue(Email.DATA, email);
   1160             operationList.add(builder.build());
   1161         }
   1162 
   1163         try {
   1164             mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
   1165         } catch (RemoteException e) {
   1166             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
   1167         } catch (OperationApplicationException e) {
   1168             Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
   1169         }
   1170     }
   1171 
   1172     private Uri lookupContact(String phone) {
   1173         Cursor c = null;
   1174         try {
   1175             Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
   1176                     Uri.encode(phone));
   1177             String[] projection = new String[] { ContactsContract.Contacts._ID,
   1178                     ContactsContract.Contacts.LOOKUP_KEY };
   1179             c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
   1180             if (c != null && c.getCount() > 0) {
   1181                 c.moveToFirst();
   1182                 int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
   1183                 int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
   1184                 String lookupKey = c.getString(lookupIdx);
   1185                 long contactId = c.getLong(idIdx);
   1186                 return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
   1187             }
   1188         } catch (Throwable t) {
   1189             Log.w(TAG, "Problem getting content resolver or performing contacts query.", t);
   1190         } finally {
   1191             if (c != null) {
   1192                 c.close();
   1193             }
   1194         }
   1195         return null;
   1196     }
   1197 
   1198     private boolean isStarred(Uri uri) {
   1199         Cursor c = null;
   1200         boolean starred = false;
   1201         try {
   1202             String[] projection = new String[] { ContactsContract.Contacts.STARRED };
   1203             c = mContext.getContentResolver().query(uri, projection, null, null, null);
   1204             if (c != null && c.getCount() > 0) {
   1205                 int starredIdx = c.getColumnIndex(ContactsContract.Contacts.STARRED);
   1206                 while (c.moveToNext()) {
   1207                     starred |= c.getInt(starredIdx) == 1;
   1208                 }
   1209             }
   1210         } catch (Throwable t) {
   1211             Log.w(TAG, "Problem getting content resolver or performing contacts query.", t);
   1212         } finally {
   1213             if (c != null) {
   1214                 c.close();
   1215             }
   1216         }
   1217         return starred;
   1218     }
   1219 
   1220     /** Search a list of notification keys for a givcen tag. */
   1221     private int findTagInKeys(String tag, List<String> orderedKeys) {
   1222         for (int i = 0; i < orderedKeys.size(); i++) {
   1223             if (orderedKeys.get(i).contains(tag)) {
   1224                 return i;
   1225             }
   1226         }
   1227         return -1;
   1228     }
   1229 }
   1230