1/* 2 * Copyright (C) 2016 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 17package com.android.dialer.database; 18 19import android.content.ContentUris; 20import android.content.ContentValues; 21import android.net.Uri; 22import android.provider.BlockedNumberContract; 23import android.provider.BlockedNumberContract.BlockedNumbers; 24import android.test.InstrumentationTestCase; 25import android.test.mock.MockContentResolver; 26import android.test.suitebuilder.annotation.SmallTest; 27 28import com.android.contacts.common.compat.CompatUtils; 29import com.android.contacts.common.test.mocks.MockContentProvider; 30import com.android.contacts.common.test.mocks.MockContentProvider.Query; 31import com.android.dialer.compat.FilteredNumberCompat; 32import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnBlockNumberListener; 33import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener; 34import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener; 35import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener; 36import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; 37import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources; 38import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes; 39 40import java.util.concurrent.CountDownLatch; 41import java.util.concurrent.TimeUnit; 42 43@SmallTest 44public class FilteredNumberAsyncQueryHandlerTest extends InstrumentationTestCase { 45 46 private static final String E164_NUMBER = "+16502530000"; 47 private static final String NUMBER = "6502530000"; 48 private static final String COUNTRY_ISO = "US"; 49 private static final Integer ID = 1; 50 private static final Integer ID2 = 2; 51 private static final Uri BLOCKED_NUMBER_URI_N = CompatUtils.isNCompatible() ? 52 Uri.withAppendedPath(BlockedNumberContract.AUTHORITY_URI, "blocked") : null; 53 private static final Uri BLOCKED_NUMBER_URI_M = 54 Uri.withAppendedPath(FilteredNumberContract.AUTHORITY_URI, "filtered_numbers_table"); 55 private static final Uri BLOCKED_NUMBER_URI = CompatUtils.isNCompatible() ? BLOCKED_NUMBER_URI_N 56 : BLOCKED_NUMBER_URI_M; 57 private static final Uri BLOCKED_NUMBER_URI_WITH_ID = 58 ContentUris.withAppendedId(BLOCKED_NUMBER_URI, ID); 59 private static final Uri EXPECTED_URI = Uri.fromParts("android", "google", "dialer"); 60 61 private final MockContentResolver mContentResolver = new MockContentResolver(); 62 private final MockContentProvider mContentProvider = new MockContentProvider(); 63 64 @Override 65 public void setUp() throws Exception { 66 super.setUp(); 67 FilteredNumberCompat.setIsEnabledForTest(true); 68 if (CompatUtils.isNCompatible()) { 69 mContentResolver.addProvider(BlockedNumberContract.AUTHORITY, mContentProvider); 70 } else { 71 mContentResolver.addProvider(FilteredNumberContract.AUTHORITY, mContentProvider); 72 } 73 } 74 75 public void testHasBlockedNumbers_Disabled() throws Throwable { 76 if (!CompatUtils.isNCompatible()) { 77 return; 78 } 79 FilteredNumberCompat.setIsEnabledForTest(false); 80 final MockContentResolver resolver = new MockContentResolver(); 81 MockContentProvider disabledProvider = new MockContentProvider(); 82 resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider); 83 84 disabledProvider.expectQuery(BLOCKED_NUMBER_URI_M).withProjection(FilteredNumberColumns._ID) 85 .withSelection(FilteredNumberColumns.TYPE + "=" 86 + FilteredNumberTypes.BLOCKED_NUMBER, null).returnRow(ID); 87 final HasBlockedNumbersListener listener = new HasBlockedNumbersListener(); 88 runTestOnUiThread(new Runnable() { 89 @Override 90 public void run() { 91 new FilteredNumberAsyncQueryHandler(resolver).hasBlockedNumbers(listener); 92 } 93 }); 94 assertTrue(listener.waitForCallback()); 95 disabledProvider.verify(); 96 } 97 98 public void testHasBlockedNumbers_NoResults() throws Throwable { 99 newHasBlockedNumbersExpectedQuery().returnEmptyCursor(); 100 final HasBlockedNumbersListener listener = new HasBlockedNumbersListener(); 101 runTestOnUiThread(new Runnable() { 102 @Override 103 public void run() { 104 new FilteredNumberAsyncQueryHandler(mContentResolver).hasBlockedNumbers(listener); 105 } 106 }); 107 assertFalse(listener.waitForCallback()); 108 mContentProvider.verify(); 109 } 110 111 public void testHasBlockedNumbers() throws Throwable { 112 newHasBlockedNumbersExpectedQuery().returnRow(ID); 113 final HasBlockedNumbersListener listener = new HasBlockedNumbersListener(); 114 runTestOnUiThread(new Runnable() { 115 @Override 116 public void run() { 117 new FilteredNumberAsyncQueryHandler(mContentResolver).hasBlockedNumbers(listener); 118 } 119 }); 120 assertTrue(listener.waitForCallback()); 121 mContentProvider.verify(); 122 } 123 124 public void testIsBlockedNumber_Disabled() throws Throwable { 125 if (!CompatUtils.isNCompatible()) { 126 return; 127 } 128 FilteredNumberCompat.setIsEnabledForTest(false); 129 final MockContentResolver resolver = new MockContentResolver(); 130 MockContentProvider disabledProvider = new MockContentProvider(); 131 resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider); 132 disabledProvider.expectQuery(BLOCKED_NUMBER_URI_M) 133 .withProjection(FilteredNumberColumns._ID, FilteredNumberColumns.TYPE) 134 .withSelection(FilteredNumberColumns.NORMALIZED_NUMBER + " = ?", E164_NUMBER) 135 .returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER); 136 final CheckBlockedListener listener = new CheckBlockedListener(); 137 runTestOnUiThread(new Runnable() { 138 @Override 139 public void run() { 140 new FilteredNumberAsyncQueryHandler(resolver) 141 .isBlockedNumber(listener, NUMBER, COUNTRY_ISO); 142 } 143 }); 144 assertEquals(ID, listener.waitForCallback()); 145 mContentProvider.verify(); 146 } 147 148 public void testIsBlockedNumber_NoResults() throws Throwable { 149 newIsBlockedNumberExpectedQuery().returnEmptyCursor(); 150 final CheckBlockedListener listener = new CheckBlockedListener(); 151 152 runTestOnUiThread(new Runnable() { 153 @Override 154 public void run() { 155 new FilteredNumberAsyncQueryHandler(mContentResolver) 156 .isBlockedNumber(listener, NUMBER, COUNTRY_ISO); 157 } 158 }); 159 assertNull(listener.waitForCallback()); 160 mContentProvider.verify(); 161 } 162 163 public void testIsBlockedNumber() throws Throwable { 164 if (CompatUtils.isNCompatible()) { 165 newIsBlockedNumberExpectedQuery().returnRow(ID); 166 } else { 167 newIsBlockedNumberExpectedQuery().returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER); 168 } 169 final CheckBlockedListener listener = new CheckBlockedListener(); 170 runTestOnUiThread(new Runnable() { 171 @Override 172 public void run() { 173 new FilteredNumberAsyncQueryHandler(mContentResolver) 174 .isBlockedNumber(listener, NUMBER, COUNTRY_ISO); 175 } 176 }); 177 assertEquals(ID, listener.waitForCallback()); 178 mContentProvider.verify(); 179 } 180 181 public void testIsBlockedNumber_MultipleResults() throws Throwable { 182 if (CompatUtils.isNCompatible()) { 183 newIsBlockedNumberExpectedQuery().returnRow(ID).returnRow(ID2); 184 } else { 185 newIsBlockedNumberExpectedQuery().returnRow(ID, FilteredNumberTypes.BLOCKED_NUMBER) 186 .returnRow(ID2, FilteredNumberTypes.BLOCKED_NUMBER); 187 } 188 final CheckBlockedListener listener = new CheckBlockedListener(); 189 runTestOnUiThread(new Runnable() { 190 @Override 191 public void run() { 192 new FilteredNumberAsyncQueryHandler(mContentResolver) 193 .isBlockedNumber(listener, NUMBER, COUNTRY_ISO); 194 } 195 }); 196 // When there are multiple matches, the first is returned 197 assertEquals(ID, listener.waitForCallback()); 198 mContentProvider.verify(); 199 } 200 201 public void testBlockNumber_Disabled() throws Throwable { 202 if (!CompatUtils.isNCompatible()) { 203 return; 204 } 205 FilteredNumberCompat.setIsEnabledForTest(false); 206 final MockContentResolver resolver = new MockContentResolver(); 207 MockContentProvider disabledProvider = new MockContentProvider(); 208 resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider); 209 210 disabledProvider.expectInsert(BLOCKED_NUMBER_URI_M, newBlockNumberContentValuesM(), 211 EXPECTED_URI); 212 final BlockNumberListener listener = new BlockNumberListener(); 213 runTestOnUiThread(new Runnable() { 214 @Override 215 public void run() { 216 new FilteredNumberAsyncQueryHandler(resolver).blockNumber(listener, E164_NUMBER, 217 NUMBER, COUNTRY_ISO); 218 } 219 }); 220 assertSame(EXPECTED_URI, listener.waitForCallback()); 221 disabledProvider.verify(); 222 } 223 224 public void testBlockNumber() throws Throwable { 225 mContentProvider.expectInsert(BLOCKED_NUMBER_URI, newBlockNumberContentValues(), 226 EXPECTED_URI); 227 final BlockNumberListener listener = new BlockNumberListener(); 228 runTestOnUiThread(new Runnable() { 229 @Override 230 public void run() { 231 new FilteredNumberAsyncQueryHandler(mContentResolver).blockNumber(listener, 232 E164_NUMBER, NUMBER, COUNTRY_ISO); 233 } 234 }); 235 assertSame(EXPECTED_URI, listener.waitForCallback()); 236 mContentProvider.verify(); 237 } 238 239 public void testBlockNumber_NullNormalizedNumber() throws Throwable { 240 mContentProvider.expectInsert(BLOCKED_NUMBER_URI, newBlockNumberContentValues(), 241 EXPECTED_URI); 242 final BlockNumberListener listener = new BlockNumberListener(); 243 runTestOnUiThread(new Runnable() { 244 @Override 245 public void run() { 246 new FilteredNumberAsyncQueryHandler(mContentResolver).blockNumber(listener, 247 NUMBER, COUNTRY_ISO); 248 } 249 }); 250 assertSame(EXPECTED_URI, listener.waitForCallback()); 251 mContentProvider.verify(); 252 } 253 254 public void testUnblockNumber_Disabled() throws Throwable { 255 if (!CompatUtils.isNCompatible()) { 256 return; 257 } 258 FilteredNumberCompat.setIsEnabledForTest(false); 259 final MockContentResolver resolver = new MockContentResolver(); 260 MockContentProvider disabledProvider = new MockContentProvider(); 261 resolver.addProvider(FilteredNumberContract.AUTHORITY, disabledProvider); 262 263 Uri uriWithId = ContentUris.withAppendedId(BLOCKED_NUMBER_URI_M, ID); 264 disabledProvider.expectQuery(uriWithId) 265 .withProjection(null) 266 .withDefaultProjection(FilteredNumberCompat.getIdColumnName()) 267 .withSelection(null, null) 268 .withSortOrder(null) 269 .returnRow(ID); 270 disabledProvider.expectDelete(uriWithId).returnRowsAffected(1); 271 final UnblockNumberListener listener = new UnblockNumberListener(); 272 runTestOnUiThread(new Runnable() { 273 @Override 274 public void run() { 275 new FilteredNumberAsyncQueryHandler(resolver).unblock(listener, ID); 276 } 277 }); 278 assertNotNull(listener.waitForCallback()); 279 disabledProvider.verify(); 280 } 281 282 public void testUnblockNumber_NullId() { 283 try { 284 new FilteredNumberAsyncQueryHandler(mContentResolver).unblock(null, (Integer) null); 285 fail(); 286 } catch (IllegalArgumentException e) {} 287 } 288 289 public void testUnblockNumber() throws Throwable { 290 mContentProvider.expectQuery(BLOCKED_NUMBER_URI_WITH_ID) 291 .withProjection(null) 292 .withDefaultProjection(FilteredNumberCompat.getIdColumnName()) 293 .withSelection(null, null) 294 .withSortOrder(null) 295 .returnRow(ID); 296 mContentProvider.expectDelete(BLOCKED_NUMBER_URI_WITH_ID).returnRowsAffected(1); 297 final UnblockNumberListener listener = new UnblockNumberListener(); 298 runTestOnUiThread(new Runnable() { 299 @Override 300 public void run() { 301 new FilteredNumberAsyncQueryHandler(mContentResolver).unblock(listener, ID); 302 } 303 }); 304 assertNotNull(listener.waitForCallback()); 305 mContentProvider.verify(); 306 } 307 308 private Query newIsBlockedNumberExpectedQuery() { 309 if (CompatUtils.isNCompatible()) { 310 return newIsBlockedNumberExpectedQueryN(); 311 } 312 return newIsBlockedNumberExpectedQueryM(); 313 } 314 315 private Query newIsBlockedNumberExpectedQueryN() { 316 return mContentProvider.expectQuery(BLOCKED_NUMBER_URI) 317 .withProjection(BlockedNumbers.COLUMN_ID) 318 .withSelection(BlockedNumbers.COLUMN_E164_NUMBER + " = ?", E164_NUMBER); 319 } 320 321 private Query newIsBlockedNumberExpectedQueryM() { 322 return mContentProvider.expectQuery(BLOCKED_NUMBER_URI) 323 .withProjection(FilteredNumberColumns._ID, FilteredNumberColumns.TYPE) 324 .withSelection(FilteredNumberColumns.NORMALIZED_NUMBER + " = ?", E164_NUMBER); 325 } 326 327 private Query newHasBlockedNumbersExpectedQuery() { 328 if (CompatUtils.isNCompatible()) { 329 return newHasBlockedNumbersExpectedQueryN(); 330 } 331 return newHasBlockedNumbersExpectedQueryM(); 332 } 333 334 private Query newHasBlockedNumbersExpectedQueryN() { 335 return mContentProvider.expectQuery(BLOCKED_NUMBER_URI) 336 .withProjection(BlockedNumbers.COLUMN_ID) 337 .withSelection(null, null); 338 } 339 340 private Query newHasBlockedNumbersExpectedQueryM() { 341 return mContentProvider.expectQuery(BLOCKED_NUMBER_URI).withProjection( 342 FilteredNumberColumns._ID) 343 .withSelection(FilteredNumberColumns.TYPE + "=" 344 + FilteredNumberTypes.BLOCKED_NUMBER, null); 345 } 346 347 private ContentValues newBlockNumberContentValues() { 348 if (CompatUtils.isNCompatible()) { 349 return newBlockNumberContentValuesN(); 350 } 351 return newBlockNumberContentValuesM(); 352 } 353 354 private ContentValues newBlockNumberContentValuesN() { 355 ContentValues contentValues = new ContentValues(); 356 contentValues.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, NUMBER); 357 return contentValues; 358 } 359 360 private ContentValues newBlockNumberContentValuesM() { 361 ContentValues contentValues = new ContentValues(); 362 contentValues.put(FilteredNumberColumns.NORMALIZED_NUMBER, E164_NUMBER); 363 contentValues.put(FilteredNumberColumns.NUMBER, NUMBER); 364 contentValues.put(FilteredNumberColumns.COUNTRY_ISO, COUNTRY_ISO); 365 contentValues.put(FilteredNumberColumns.TYPE, FilteredNumberTypes.BLOCKED_NUMBER); 366 contentValues.put(FilteredNumberColumns.SOURCE, FilteredNumberSources.USER); 367 return contentValues; 368 } 369 370 private class CheckBlockedListener implements OnCheckBlockedListener { 371 public final CountDownLatch onCheckCompleteCalled; 372 public Integer id; 373 374 public CheckBlockedListener() { 375 onCheckCompleteCalled = new CountDownLatch(1); 376 } 377 378 @Override 379 public void onCheckComplete(Integer id) { 380 this.id = id; 381 onCheckCompleteCalled.countDown(); 382 } 383 384 public Integer waitForCallback() throws InterruptedException { 385 if (!onCheckCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) { 386 throw new IllegalStateException("Waiting on callback timed out."); 387 } 388 return id; 389 } 390 } 391 392 private class HasBlockedNumbersListener implements OnHasBlockedNumbersListener { 393 public final CountDownLatch onHasBlockedNumbersCalled; 394 public boolean hasBlockedNumbers; 395 396 public HasBlockedNumbersListener() { 397 onHasBlockedNumbersCalled = new CountDownLatch(1); 398 } 399 400 @Override 401 public void onHasBlockedNumbers(boolean hasBlockedNumbers) { 402 this.hasBlockedNumbers = hasBlockedNumbers; 403 onHasBlockedNumbersCalled.countDown(); 404 } 405 406 public boolean waitForCallback() throws InterruptedException { 407 if (!onHasBlockedNumbersCalled.await(5000, TimeUnit.MILLISECONDS)) { 408 throw new IllegalStateException("Waiting on callback timed out."); 409 } 410 return hasBlockedNumbers; 411 } 412 } 413 414 private class BlockNumberListener implements OnBlockNumberListener { 415 public final CountDownLatch onBlockCompleteCalled; 416 public Uri uri; 417 418 public BlockNumberListener() { 419 onBlockCompleteCalled = new CountDownLatch(1); 420 } 421 422 @Override 423 public void onBlockComplete(Uri uri) { 424 this.uri = uri; 425 onBlockCompleteCalled.countDown(); 426 } 427 428 public Uri waitForCallback() throws InterruptedException { 429 if (!onBlockCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) { 430 throw new IllegalStateException("Waiting on callback timed out."); 431 } 432 return uri; 433 } 434 } 435 436 private class UnblockNumberListener implements OnUnblockNumberListener { 437 public final CountDownLatch onUnblockCompleteCalled; 438 public Integer result; 439 440 public UnblockNumberListener() { 441 onUnblockCompleteCalled = new CountDownLatch(1); 442 } 443 444 @Override 445 public void onUnblockComplete(int rows, ContentValues values) { 446 result = rows; 447 onUnblockCompleteCalled.countDown(); 448 } 449 450 public Integer waitForCallback() throws InterruptedException { 451 if (!onUnblockCompleteCalled.await(5000, TimeUnit.MILLISECONDS)) { 452 throw new IllegalStateException("Waiting on callback timed out."); 453 } 454 return result; 455 } 456 } 457} 458