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.server.telecom.tests; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.drawable.BitmapDrawable; 22import android.graphics.drawable.Drawable; 23import android.net.Uri; 24import android.test.suitebuilder.annotation.SmallTest; 25import android.telecom.Logging.Session; 26 27import com.android.internal.telephony.CallerInfo; 28import com.android.internal.telephony.CallerInfoAsyncQuery; 29import com.android.server.telecom.Call; 30import com.android.server.telecom.CallerInfoAsyncQueryFactory; 31import com.android.server.telecom.CallerInfoLookupHelper; 32import com.android.server.telecom.ContactsAsyncHelper; 33import com.android.server.telecom.TelecomSystem; 34 35import org.mockito.ArgumentCaptor; 36import org.mockito.Mock; 37 38import java.io.FileNotFoundException; 39import java.io.InputStream; 40import java.net.URI; 41import java.util.concurrent.CountDownLatch; 42 43import static org.mockito.Matchers.any; 44import static org.mockito.Matchers.anyInt; 45import static org.mockito.Matchers.anyString; 46import static org.mockito.Matchers.eq; 47import static org.mockito.Matchers.isNull; 48import static org.mockito.Mockito.atMost; 49import static org.mockito.Mockito.mock; 50import static org.mockito.Mockito.times; 51import static org.mockito.Mockito.verify; 52import static org.mockito.Mockito.when; 53 54public class CallerInfoLookupHelperTest extends TelecomTestCase { 55 @Mock Context mContext; 56 @Mock CallerInfoAsyncQueryFactory mFactory; 57 @Mock ContactsAsyncHelper mContactsAsyncHelper; 58 @Mock Drawable mDrawable2; 59 60 CallerInfo mCallerInfo1; 61 CallerInfo mCallerInfo2; 62 63 @Mock Drawable mDrawable1; 64 CallerInfoLookupHelper mCallerInfoLookupHelper; 65 static final Uri URI1 = Uri.parse("tel:555-555-7010"); 66 static final Uri URI2 = Uri.parse("tel:555-555-7016"); 67 68 static final Uri CONTACTS_PHOTO_URI = Uri.parse( 69 "android.resource://com.android.server.telecom.tests/" 70 + R.drawable.contacts_sample_photo_small); 71 72 Bitmap mBitmap; 73 74 @Override 75 public void setUp() throws Exception { 76 super.setUp(); 77 mCallerInfoLookupHelper = new CallerInfoLookupHelper(mContext, 78 mFactory, mContactsAsyncHelper, new TelecomSystem.SyncRoot() { }); 79 when(mFactory.startQuery(anyInt(), eq(mContext), anyString(), 80 any(CallerInfoAsyncQuery.OnQueryCompleteListener.class), any())) 81 .thenReturn(mock(CallerInfoAsyncQuery.class)); 82 mCallerInfo1 = new CallerInfo(); 83 mCallerInfo2 = new CallerInfo(); 84 85 if (mBitmap == null) { 86 InputStream is; 87 try { 88 is = getTestContext().getContentResolver().openInputStream(CONTACTS_PHOTO_URI); 89 } catch (FileNotFoundException e) { 90 return; 91 } 92 93 Drawable d = Drawable.createFromStream(is, CONTACTS_PHOTO_URI.toString()); 94 mBitmap = ((BitmapDrawable) d).getBitmap(); 95 } 96 } 97 98 @SmallTest 99 public void testLookupWithEmptyHandle() { 100 CallerInfoLookupHelper.OnQueryCompleteListener listener = mock( 101 CallerInfoLookupHelper.OnQueryCompleteListener.class); 102 mCallerInfoLookupHelper.startLookup(Uri.EMPTY, listener); 103 104 verify(listener).onCallerInfoQueryComplete(eq(Uri.EMPTY), isNull(CallerInfo.class)); 105 verifyProperCleanup(); 106 } 107 108 public void testSimpleLookup() { 109 CallerInfoLookupHelper.OnQueryCompleteListener listener = mock( 110 CallerInfoLookupHelper.OnQueryCompleteListener.class); 111 mCallerInfo1.contactDisplayPhotoUri = CONTACTS_PHOTO_URI; 112 113 mCallerInfoLookupHelper.startLookup(URI1, listener); 114 waitForActionCompletion(); 115 116 // CallerInfo section 117 ArgumentCaptor<CallerInfoAsyncQuery.OnQueryCompleteListener> queryListenerCaptor = 118 ArgumentCaptor.forClass(CallerInfoAsyncQuery.OnQueryCompleteListener.class); 119 ArgumentCaptor<Session> logSessionCaptor = ArgumentCaptor.forClass(Session.class); 120 verify(mFactory).startQuery(anyInt(), eq(mContext), eq(URI1.getSchemeSpecificPart()), 121 queryListenerCaptor.capture(), logSessionCaptor.capture()); 122 123 queryListenerCaptor.getValue().onQueryComplete( 124 0, logSessionCaptor.getValue(), mCallerInfo1); 125 verify(listener).onCallerInfoQueryComplete(URI1, mCallerInfo1); 126 waitForActionCompletion(); 127 128 // Contacts photo section 129 ArgumentCaptor<ContactsAsyncHelper.OnImageLoadCompleteListener> imageListenerCaptor = 130 ArgumentCaptor.forClass(ContactsAsyncHelper.OnImageLoadCompleteListener.class); 131 verify(mContactsAsyncHelper).startObtainPhotoAsync(anyInt(), eq(mContext), 132 eq(CONTACTS_PHOTO_URI), imageListenerCaptor.capture(), logSessionCaptor.capture()); 133 134 imageListenerCaptor.getValue().onImageLoadComplete(0, mDrawable1, mBitmap, 135 logSessionCaptor.getValue()); 136 verify(listener).onContactPhotoQueryComplete(URI1, mCallerInfo1); 137 assertEquals(mDrawable1, mCallerInfo1.cachedPhoto); 138 assertEquals(mBitmap, mCallerInfo1.cachedPhotoIcon); 139 140 verifyProperCleanup(); 141 } 142 143 public void testLookupWithTwoListeners() { 144 CallerInfoLookupHelper.OnQueryCompleteListener callListener = mock( 145 CallerInfoLookupHelper.OnQueryCompleteListener.class); 146 CallerInfoLookupHelper.OnQueryCompleteListener otherListener = mock( 147 CallerInfoLookupHelper.OnQueryCompleteListener.class); 148 mCallerInfo1.contactDisplayPhotoUri = CONTACTS_PHOTO_URI; 149 150 mCallerInfoLookupHelper.startLookup(URI1, callListener); 151 mCallerInfoLookupHelper.startLookup(URI1, otherListener); 152 waitForActionCompletion(); 153 154 ArgumentCaptor<CallerInfoAsyncQuery.OnQueryCompleteListener> queryListenerCaptor = 155 ArgumentCaptor.forClass(CallerInfoAsyncQuery.OnQueryCompleteListener.class); 156 ArgumentCaptor<Session> logSessionCaptor = ArgumentCaptor.forClass(Session.class); 157 verify(mFactory, times(1)).startQuery(anyInt(), eq(mContext), 158 eq(URI1.getSchemeSpecificPart()), queryListenerCaptor.capture(), 159 logSessionCaptor.capture()); 160 161 queryListenerCaptor.getValue().onQueryComplete( 162 0, logSessionCaptor.getValue(), mCallerInfo1); 163 verify(callListener, times(1)).onCallerInfoQueryComplete(URI1, mCallerInfo1); 164 verify(otherListener, times(1)).onCallerInfoQueryComplete(URI1, mCallerInfo1); 165 waitForActionCompletion(); 166 167 ArgumentCaptor<ContactsAsyncHelper.OnImageLoadCompleteListener> imageListenerCaptor = 168 ArgumentCaptor.forClass(ContactsAsyncHelper.OnImageLoadCompleteListener.class); 169 verify(mContactsAsyncHelper).startObtainPhotoAsync(anyInt(), eq(mContext), 170 eq(CONTACTS_PHOTO_URI), imageListenerCaptor.capture(), logSessionCaptor.capture()); 171 172 imageListenerCaptor.getValue().onImageLoadComplete(0, mDrawable1, mBitmap, 173 logSessionCaptor.getValue()); 174 verify(callListener).onContactPhotoQueryComplete(URI1, mCallerInfo1); 175 verify(otherListener).onContactPhotoQueryComplete(URI1, mCallerInfo1); 176 assertEquals(mDrawable1, mCallerInfo1.cachedPhoto); 177 assertEquals(mBitmap, mCallerInfo1.cachedPhotoIcon); 178 179 verifyProperCleanup(); 180 } 181 182 public void testListenerAddedAfterCallerInfoBeforePhoto() { 183 CallerInfoLookupHelper.OnQueryCompleteListener callListener = mock( 184 CallerInfoLookupHelper.OnQueryCompleteListener.class); 185 CallerInfoLookupHelper.OnQueryCompleteListener otherListener = mock( 186 CallerInfoLookupHelper.OnQueryCompleteListener.class); 187 mCallerInfo1.contactDisplayPhotoUri = CONTACTS_PHOTO_URI; 188 189 mCallerInfoLookupHelper.startLookup(URI1, callListener); 190 waitForActionCompletion(); 191 192 ArgumentCaptor<CallerInfoAsyncQuery.OnQueryCompleteListener> queryListenerCaptor = 193 ArgumentCaptor.forClass(CallerInfoAsyncQuery.OnQueryCompleteListener.class); 194 ArgumentCaptor<Session> logSessionCaptor = ArgumentCaptor.forClass(Session.class); 195 verify(mFactory, times(1)).startQuery(anyInt(), eq(mContext), 196 eq(URI1.getSchemeSpecificPart()), queryListenerCaptor.capture(), 197 logSessionCaptor.capture()); 198 199 queryListenerCaptor.getValue().onQueryComplete( 200 0, logSessionCaptor.getValue(), mCallerInfo1); 201 verify(callListener, times(1)).onCallerInfoQueryComplete(URI1, mCallerInfo1); 202 waitForActionCompletion(); 203 204 ArgumentCaptor<ContactsAsyncHelper.OnImageLoadCompleteListener> imageListenerCaptor = 205 ArgumentCaptor.forClass(ContactsAsyncHelper.OnImageLoadCompleteListener.class); 206 verify(mContactsAsyncHelper).startObtainPhotoAsync(anyInt(), eq(mContext), 207 eq(CONTACTS_PHOTO_URI), imageListenerCaptor.capture(), logSessionCaptor.capture()); 208 mCallerInfoLookupHelper.startLookup(URI1, otherListener); 209 verify(otherListener, times(1)).onCallerInfoQueryComplete(URI1, mCallerInfo1); 210 211 imageListenerCaptor.getValue().onImageLoadComplete(0, mDrawable1, mBitmap, 212 logSessionCaptor.getValue()); 213 verify(callListener).onContactPhotoQueryComplete(URI1, mCallerInfo1); 214 verify(otherListener).onContactPhotoQueryComplete(URI1, mCallerInfo1); 215 assertEquals(mDrawable1, mCallerInfo1.cachedPhoto); 216 assertEquals(mBitmap, mCallerInfo1.cachedPhotoIcon); 217 218 verifyProperCleanup(); 219 } 220 221 private void verifyProperCleanup() { 222 assertEquals(0, mCallerInfoLookupHelper.getCallerInfoEntries().size()); 223 } 224 225 private void waitForActionCompletion() { 226 final CountDownLatch lock = new CountDownLatch(1); 227 mCallerInfoLookupHelper.getHandler().post(lock::countDown); 228 while (lock.getCount() > 0) { 229 try { 230 lock.await(); 231 } catch (InterruptedException e) { 232 // do nothing 233 } 234 } 235 } 236} 237