1 /* 2 * Copyright (C) 2017 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.bluetooth.pbapclient; 18 19 import android.accounts.Account; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.content.res.Resources; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.provider.CallLog.Calls; 26 import android.provider.ContactsContract; 27 import android.support.test.InstrumentationRegistry; 28 import android.support.test.filters.MediumTest; 29 import android.support.test.runner.AndroidJUnit4; 30 31 import com.android.bluetooth.R; 32 33 import org.junit.Assert; 34 import org.junit.Assume; 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.util.HashMap; 42 import java.util.TimeZone; 43 44 @MediumTest 45 @RunWith(AndroidJUnit4.class) 46 public class PbapParserTest { 47 private Account mAccount; 48 private Resources mTestResources; 49 private Context mTargetContext; 50 private static final String TEST_ACCOUNT_NAME = "PBAPTESTACCOUNT"; 51 private static final String TEST_PACKAGE_NAME = "com.android.bluetooth.tests"; 52 53 @Before 54 public void setUp() { 55 mTargetContext = InstrumentationRegistry.getTargetContext(); 56 Assume.assumeTrue("Ignore test when PbapClientService is not enabled", 57 mTargetContext.getResources().getBoolean(R.bool.profile_supported_pbapclient)); 58 mAccount = new Account(TEST_ACCOUNT_NAME, 59 mTargetContext.getString(com.android.bluetooth.R.string.pbap_account_type)); 60 try { 61 mTestResources = mTargetContext.getPackageManager() 62 .getResourcesForApplication(TEST_PACKAGE_NAME); 63 } catch (PackageManager.NameNotFoundException e) { 64 Assert.fail("Setup Failure Unable to get resources" + e.toString()); 65 } 66 cleanupCallLog(); 67 cleanupPhonebook(); 68 } 69 70 // testNoTimestamp should parse 1 poorly formed vcard and not crash. 71 @Test 72 public void testNoTimestamp() throws IOException { 73 InputStream fileStream; 74 fileStream = mTestResources.openRawResource( 75 com.android.bluetooth.tests.R.raw.no_timestamp_call_log); 76 BluetoothPbapVcardList pbapVCardList = new BluetoothPbapVcardList(mAccount, fileStream, 77 PbapClientConnectionHandler.VCARD_TYPE_30); 78 Assert.assertEquals(1, pbapVCardList.getCount()); 79 CallLogPullRequest processor = 80 new CallLogPullRequest(mTargetContext, PbapClientConnectionHandler.MCH_PATH, 81 new HashMap<>(), mAccount); 82 processor.setResults(pbapVCardList.getList()); 83 84 // Verify that these entries aren't in the call log to start. 85 Assert.assertFalse(verifyCallLog("555-0001", null, "3")); 86 87 // Finish processing the data and verify entries were added to the call log. 88 processor.onPullComplete(); 89 Assert.assertTrue(verifyCallLog("555-0001", null, "3")); 90 } 91 92 // testMissedCall should parse one phonecall correctly. 93 @Test 94 public void testMissedCall() throws IOException { 95 InputStream fileStream; 96 fileStream = mTestResources.openRawResource( 97 com.android.bluetooth.tests.R.raw.single_missed_call); 98 BluetoothPbapVcardList pbapVCardList = new BluetoothPbapVcardList(mAccount, fileStream, 99 PbapClientConnectionHandler.VCARD_TYPE_30); 100 Assert.assertEquals(1, pbapVCardList.getCount()); 101 CallLogPullRequest processor = 102 new CallLogPullRequest(mTargetContext, PbapClientConnectionHandler.MCH_PATH, 103 new HashMap<>(), mAccount); 104 processor.setResults(pbapVCardList.getList()); 105 106 // Verify that these entries aren't in the call log to start. 107 Assert.assertFalse(verifyCallLog("555-0002", "1483232460000", "3")); 108 // Finish processing the data and verify entries were added to the call log. 109 processor.onPullComplete(); 110 Assert.assertTrue(verifyCallLog("555-0002", "1483232460000", "3")); 111 } 112 113 // testUnknownCall should parse two calls with no phone number. 114 @Test 115 public void testUnknownCall() throws IOException { 116 InputStream fileStream; 117 fileStream = mTestResources.openRawResource( 118 com.android.bluetooth.tests.R.raw.unknown_number_call); 119 BluetoothPbapVcardList pbapVCardList = new BluetoothPbapVcardList(mAccount, fileStream, 120 PbapClientConnectionHandler.VCARD_TYPE_30); 121 Assert.assertEquals(2, pbapVCardList.getCount()); 122 CallLogPullRequest processor = 123 new CallLogPullRequest(mTargetContext, PbapClientConnectionHandler.MCH_PATH, 124 new HashMap<>(), mAccount); 125 processor.setResults(pbapVCardList.getList()); 126 127 // Verify that these entries aren't in the call log to start. 128 Assert.assertFalse(verifyCallLog("", "1483232520000", "3")); 129 Assert.assertFalse(verifyCallLog("", "1483232580000", "3")); 130 131 // Finish processing the data and verify entries were added to the call log. 132 processor.onPullComplete(); 133 Assert.assertTrue(verifyCallLog("", "1483232520000", "3")); 134 Assert.assertTrue(verifyCallLog("", "1483232580000", "3")); 135 } 136 137 @Test 138 public void testPullPhoneBook() throws IOException { 139 InputStream fileStream; 140 fileStream = mTestResources.openRawResource( 141 com.android.bluetooth.tests.R.raw.v30_simple); 142 BluetoothPbapVcardList pbapVCardList = new BluetoothPbapVcardList(mAccount, fileStream, 143 PbapClientConnectionHandler.VCARD_TYPE_30); 144 Assert.assertEquals(1, pbapVCardList.getCount()); 145 PhonebookPullRequest processor = new PhonebookPullRequest(mTargetContext, mAccount); 146 processor.setResults(pbapVCardList.getList()); 147 Assert.assertFalse(verifyPhonebook("Roid And", "0300000000")); 148 processor.onPullComplete(); 149 Assert.assertTrue(verifyPhonebook("Roid And", "0300000000")); 150 } 151 152 private void cleanupCallLog() { 153 mTargetContext.getContentResolver().delete(Calls.CONTENT_URI, null, null); 154 } 155 156 private void cleanupPhonebook() { 157 mTargetContext.getContentResolver().delete(ContactsContract.RawContacts.CONTENT_URI, 158 null, null); 159 } 160 161 // Find Entries in call log with type matching number and date. 162 // If number or date is null it will match any number or date respectively. 163 private boolean verifyCallLog(String number, String date, String type) { 164 String[] query = new String[]{Calls.NUMBER, Calls.DATE, Calls.TYPE}; 165 Cursor cursor = mTargetContext.getContentResolver() 166 .query(Calls.CONTENT_URI, query, Calls.TYPE + "= " + type, null, 167 Calls.DATE + ", " + Calls.NUMBER); 168 if (date != null) { 169 date = adjDate(date); 170 } 171 if (cursor != null) { 172 while (cursor.moveToNext()) { 173 String foundNumber = cursor.getString(cursor.getColumnIndex(Calls.NUMBER)); 174 String foundDate = cursor.getString(cursor.getColumnIndex(Calls.DATE)); 175 if ((number == null || number.equals(foundNumber)) && (date == null || date.equals( 176 foundDate))) { 177 return true; 178 } 179 } 180 cursor.close(); 181 } 182 return false; 183 } 184 185 // Get time zone from device and adjust date to the device's time zone. 186 private static String adjDate(String date) { 187 TimeZone tz = TimeZone.getDefault(); 188 long dt = Long.valueOf(date) - tz.getRawOffset(); 189 return Long.toString(dt); 190 } 191 192 private boolean verifyPhonebook(String name, String number) { 193 Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, 194 Uri.encode(number)); 195 Cursor c = mTargetContext.getContentResolver().query(uri, null, null, null); 196 if (c != null && c.getCount() > 0) { 197 c.moveToNext(); 198 String displayName = c.getString( 199 c.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)); 200 if (displayName.equals(name)) { 201 return true; 202 } 203 } 204 return false; 205 } 206 207 } 208