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