1 /*
2  * Copyright (C) 2020 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 android.provider.cts.contacts;
18 
19 import android.content.ContentProviderClient;
20 import android.content.ContentResolver;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.database.ContentObserver;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.platform.test.annotations.AsbSecurityTest;
27 import android.provider.CallLog;
28 import android.provider.CallLog.Calls;
29 import android.test.InstrumentationTestCase;
30 
31 import java.util.concurrent.CountDownLatch;
32 import java.util.concurrent.TimeUnit;
33 
34 public class CallLogProviderTest extends InstrumentationTestCase {
35     private ContentResolver mContentResolver;
36     private ContentProviderClient mProvider;
37 
38     private static final String TEST_NUMBER = "5551234";
39     private static final int TIME_OUT_MILLIS = 5000;
40 
41     @Override
setUp()42     protected void setUp() throws Exception {
43         super.setUp();
44         mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
45         mProvider = mContentResolver.acquireContentProviderClient(CallLog.AUTHORITY);
46     }
47 
48     @AsbSecurityTest(cveBugId = 143230980)
testNoSubqueries()49     public void testNoSubqueries() throws Exception {
50         // Add a single call just to make sure the call log has something inside
51         ContentValues values = new ContentValues();
52         values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
53         values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
54         values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
55         values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
56 
57         mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
58 
59         // Attempt to do a query that contains a subquery -- this should fail since this test does
60         // not have READ_VOICEMAIL.
61         try {
62             Cursor c = mProvider.query(Calls.CONTENT_URI, null, CallLog.Calls.NUMBER + " = ?",
63                     new String[]{TEST_NUMBER},
64                     "date DESC LIMIT (SELECT count(*) + 1 FROM calls WHERE type = 4");
65             assertEquals(0, c.getCount());
66         } catch (IllegalArgumentException e) {
67             // expected/tolerated
68         }
69     }
70 
testUpdate()71     public void testUpdate() throws Exception {
72         // Add a single call just to make sure the call log has something inside
73         ContentValues values = new ContentValues();
74         values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
75         values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
76         values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
77         values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
78         Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
79         // Get last modified prior to modification.
80         long lastModified = getLastModified(uri);
81 
82         CountDownLatch changeLatch = new CountDownLatch(1);
83         mContentResolver.registerContentObserver(
84                 CallLog.Calls.CONTENT_URI, true,
85                 new ContentObserver(null /* handler */) {
86                     @Override
87                     public void onChange(boolean selfChange, Uri uri) {
88                         mContentResolver.unregisterContentObserver(this);
89                         changeLatch.countDown();
90                         super.onChange(selfChange);
91                     }
92                 });
93 
94         // Update it!
95         values.put(CallLog.Calls.DURATION, Long.valueOf(6 /*call duration*/));
96         int numUpdated = mContentResolver.update(uri, values, null, null);
97         assertEquals(1, numUpdated);
98         try {
99             assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS));
100         } catch (InterruptedException e) {
101             e.printStackTrace();
102             fail("Expected update notification.");
103         }
104 
105         // Verify last modified is > than the original value.
106         long newLastModified = getLastModified(uri);
107         assertTrue(newLastModified > lastModified);
108     }
109 
getLastModified(Uri uri)110     public long getLastModified(Uri uri) {
111         // Query the data base to get the last updated.
112         String[] projection = new String[] {
113                 Calls.LAST_MODIFIED
114         };
115         Cursor results = mContentResolver.query(uri, projection, null, null, null, null);
116         results.moveToFirst();
117         assertEquals(1, results.getCount());
118         return results.getLong(results.getColumnIndex(Calls.LAST_MODIFIED));
119     }
120 
testDelete()121     public void testDelete() throws Exception {
122         // Add a single call just to make sure the call log has something inside
123         ContentValues values = new ContentValues();
124         values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
125         values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
126         values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
127         values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
128         Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
129 
130         CountDownLatch changeLatch = new CountDownLatch(1);
131         mContentResolver.registerContentObserver(
132                 CallLog.Calls.CONTENT_URI, true,
133                 new ContentObserver(null /* handler */) {
134                     @Override
135                     public void onChange(boolean selfChange, Uri uri) {
136                         mContentResolver.unregisterContentObserver(this);
137                         changeLatch.countDown();
138                         super.onChange(selfChange);
139                     }
140                 });
141 
142         // Delete it.
143         // Yuck, you can't just delete using the uri passed in; you need to build a where clause.
144         int count = mContentResolver.delete(Calls.CONTENT_URI, Calls._ID + "="
145                 + ContentUris.parseId(uri), null);
146         assertEquals(1, count);
147         try {
148             assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS));
149         } catch (InterruptedException e) {
150             e.printStackTrace();
151             fail("Expected update notification.");
152         }
153     }
154 }
155