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