1 /*
2  * Copyright (C) 2019 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.cellbroadcastservice.tests;
18 
19 import static com.android.cellbroadcastservice.CellBroadcastProvider.QUERY_COLUMNS;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.Mockito.doReturn;
24 
25 import android.content.ContentValues;
26 import android.content.pm.PackageManager;
27 import android.database.Cursor;
28 import android.net.Uri;
29 import android.provider.Telephony.CellBroadcasts;
30 import android.test.mock.MockContentResolver;
31 import android.test.mock.MockContext;
32 import android.util.Log;
33 
34 import com.android.cellbroadcastservice.CellBroadcastProvider;
35 import com.android.cellbroadcastservice.CellBroadcastProvider.CellBroadcastPermissionChecker;
36 
37 import junit.framework.TestCase;
38 
39 import org.junit.Test;
40 import org.mockito.Mock;
41 import org.mockito.MockitoAnnotations;
42 
43 public class CellBroadcastProviderTest extends TestCase {
44     private static final String TAG = CellBroadcastProviderTest.class.getSimpleName();
45 
46     public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
47 
48     private static final int GEO_SCOPE = 1;
49     private static final String PLMN = "123456";
50     private static final int LAC = 13;
51     private static final int CID = 123;
52     private static final int SERIAL_NUMBER = 17984;
53     private static final int SERVICE_CATEGORY = 4379;
54     private static final String LANGUAGE_CODE = "en";
55     private static final String MESSAGE_BODY = "AMBER Alert: xxxx";
56     private static final int MESSAGE_FORMAT = 1;
57     private static final int MESSAGE_PRIORITY = 3;
58     private static final int ETWS_WARNING_TYPE = 1;
59     private static final int ETWS_IS_PRIMARY = 0;
60     private static final int CMAS_MESSAGE_CLASS = 1;
61     private static final int CMAS_CATEGORY = 6;
62     private static final int CMAS_RESPONSE_TYPE = 1;
63     private static final int CMAS_SEVERITY = 2;
64     private static final int CMAS_URGENCY = 3;
65     private static final int CMAS_CERTAINTY = 4;
66     private static final int RECEIVED_TIME = 1562792637;
67     private static final int MESSAGE_BROADCASTED = 1;
68     private static final int MESSAGE_NOT_DISPLAYED = 0;
69     private static final String GEOMETRIES_COORDINATES = "polygon|0,0|0,1|1,1|1,0;circle|0,0|100";
70 
71     private static final String SELECT_BY_ID = CellBroadcasts._ID + "=?";
72 
73     private CellBroadcastProviderTestable mCellBroadcastProviderTestable;
74     private MockContextWithProvider mContext;
75     private MockContentResolver mContentResolver;
76 
77     @Mock
78     private CellBroadcastPermissionChecker mMockPermissionChecker;
79 
80     @Override
setUp()81     protected void setUp() throws Exception {
82         super.setUp();
83         MockitoAnnotations.initMocks(this);
84         doReturn(true).when(mMockPermissionChecker).hasFullAccessPermission();
85 
86         mCellBroadcastProviderTestable = new CellBroadcastProviderTestable(mMockPermissionChecker);
87         mContext = new MockContextWithProvider(mCellBroadcastProviderTestable);
88         mContentResolver = mContext.getContentResolver();
89     }
90 
91     @Override
tearDown()92     protected void tearDown() throws Exception {
93         mCellBroadcastProviderTestable.closeDatabase();
94         super.tearDown();
95     }
96 
97     @Test
testUpdate()98     public void testUpdate() {
99         // Insert a cellbroadcast to the database.
100         ContentValues cv = fakeCellBroadcast();
101         Uri uri = mContentResolver.insert(CONTENT_URI, cv);
102         assertThat(uri).isNotNull();
103 
104         // Change some fields of this cell broadcast.
105         int messageDisplayed = 1 - cv.getAsInteger(CellBroadcasts.MESSAGE_DISPLAYED);
106         int receivedTime = 1234555555;
107         cv.put(CellBroadcasts.MESSAGE_DISPLAYED, messageDisplayed);
108         cv.put(CellBroadcasts.RECEIVED_TIME, receivedTime);
109         mContentResolver.update(CONTENT_URI, cv, SELECT_BY_ID,
110                 new String[] { uri.getLastPathSegment() });
111 
112         // Query and check if the update is succeeded.
113         Cursor cursor = mContentResolver.query(CONTENT_URI, QUERY_COLUMNS,
114                 SELECT_BY_ID, new String[] { uri.getLastPathSegment() }, null /* orderBy */);
115         cursor.moveToNext();
116 
117         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME)))
118                 .isEqualTo(receivedTime);
119         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_DISPLAYED)))
120                 .isEqualTo(messageDisplayed);
121         cursor.close();
122     }
123 
124     @Test
testUpdate_WithoutWritePermission_fail()125     public void testUpdate_WithoutWritePermission_fail() {
126         ContentValues cv = fakeCellBroadcast();
127         Uri uri = mContentResolver.insert(CONTENT_URI, cv);
128         assertThat(uri).isNotNull();
129 
130         // Revoke the write permission
131         doReturn(false).when(mMockPermissionChecker).hasFullAccessPermission();
132 
133         try {
134             mContentResolver.update(CONTENT_URI, cv, SELECT_BY_ID,
135                     new String[] { uri.getLastPathSegment() });
136             fail();
137         } catch (SecurityException ex) {
138             // pass the test
139         }
140     }
141 
142     @Test
testGetAllCellBroadcast()143     public void testGetAllCellBroadcast() {
144         // Insert some cell broadcasts which message_displayed is false
145         int messageNotDisplayedCount = 5;
146         ContentValues cv = fakeCellBroadcast();
147         cv.put(CellBroadcasts.MESSAGE_DISPLAYED, 0);
148         for (int i = 0; i < messageNotDisplayedCount; i++) {
149             mContentResolver.insert(CONTENT_URI, cv);
150         }
151 
152         // Insert some cell broadcasts which message_displayed is true
153         int messageDisplayedCount = 6;
154         cv.put(CellBroadcasts.MESSAGE_DISPLAYED, 1);
155         for (int i = 0; i < messageDisplayedCount; i++) {
156             mContentResolver.insert(CONTENT_URI, cv);
157         }
158 
159         // Query the broadcast with message_displayed is false
160         Cursor cursor = mContentResolver.query(
161                 CONTENT_URI,
162                 QUERY_COLUMNS,
163                 String.format("%s=?", CellBroadcasts.MESSAGE_DISPLAYED), /* selection */
164                 new String[] {"0"}, /* selectionArgs */
165                 null /* sortOrder */);
166         assertThat(cursor.getCount()).isEqualTo(messageNotDisplayedCount);
167     }
168 
169     @Test
testDelete_withoutWritePermission_throwSecurityException()170     public void testDelete_withoutWritePermission_throwSecurityException() {
171         Uri uri = mContentResolver.insert(CONTENT_URI, fakeCellBroadcast());
172         assertThat(uri).isNotNull();
173 
174         // Revoke the write permission
175         doReturn(false).when(mMockPermissionChecker).hasFullAccessPermission();
176 
177         try {
178             mContentResolver.delete(CONTENT_URI, SELECT_BY_ID,
179                     new String[] { uri.getLastPathSegment() });
180             fail();
181         } catch (SecurityException ex) {
182             // pass the test
183         }
184     }
185 
186 
187     @Test
testDelete_oneRecord_success()188     public void testDelete_oneRecord_success() {
189         // Insert a cellbroadcast to the database.
190         ContentValues cv = fakeCellBroadcast();
191         Uri uri = mContentResolver.insert(CONTENT_URI, cv);
192         assertThat(uri).isNotNull();
193 
194         String[] selectionArgs = new String[] { uri.getLastPathSegment() };
195 
196         // Ensure the cell broadcast is inserted.
197         Cursor cursor = mContentResolver.query(CONTENT_URI, QUERY_COLUMNS,
198                 SELECT_BY_ID, selectionArgs, null /* orderBy */);
199         assertThat(cursor.getCount()).isEqualTo(1);
200         cursor.close();
201 
202         // Delete the cell broadcast
203         int rowCount = mContentResolver.delete(CONTENT_URI, SELECT_BY_ID,
204                 selectionArgs);
205         assertThat(rowCount).isEqualTo(1);
206 
207         // Ensure the cell broadcast is deleted.
208         cursor = mContentResolver.query(CONTENT_URI, QUERY_COLUMNS, SELECT_BY_ID,
209                 selectionArgs, null /* orderBy */);
210         assertThat(cursor.getCount()).isEqualTo(0);
211         cursor.close();
212     }
213 
214     @Test
testDelete_all_success()215     public void testDelete_all_success() {
216         // Insert a cellbroadcast to the database.
217         mContentResolver.insert(CONTENT_URI, fakeCellBroadcast());
218         mContentResolver.insert(CONTENT_URI, fakeCellBroadcast());
219 
220         // Ensure the cell broadcast are inserted.
221         Cursor cursor = mContentResolver.query(CONTENT_URI, QUERY_COLUMNS,
222                 null /* selection */, null /* selectionArgs */, null /* orderBy */);
223         assertThat(cursor.getCount()).isEqualTo(2);
224         cursor.close();
225 
226         // Delete all cell broadcasts.
227         int rowCount = mContentResolver.delete(
228                 CONTENT_URI, null /* selection */, null /* selectionArgs */);
229         assertThat(rowCount).isEqualTo(2);
230         cursor.close();
231 
232         // Ensure all cell broadcasts are deleted.
233         cursor = mContentResolver.query(CONTENT_URI, QUERY_COLUMNS,
234                 null /* selection */, null /* selectionArgs */, null /* orderBy */);
235         assertThat(cursor.getCount()).isEqualTo(0);
236         cursor.close();
237     }
238 
239     @Test
testInsert_withoutWritePermission_fail()240     public void testInsert_withoutWritePermission_fail() {
241         doReturn(false).when(mMockPermissionChecker).hasFullAccessPermission();
242 
243         try {
244             mContentResolver.insert(CONTENT_URI, fakeCellBroadcast());
245             fail();
246         } catch (SecurityException ex) {
247             // pass the test
248         }
249     }
250 
251     @Test
testInsertAndQuery()252     public void testInsertAndQuery() {
253         // Insert a cell broadcast message
254         Uri uri = mContentResolver.insert(CONTENT_URI, fakeCellBroadcast());
255 
256         // Verify that the return uri is not null and the record is inserted into the database
257         // correctly.
258         assertThat(uri).isNotNull();
259         Cursor cursor = mContentResolver.query(
260                 CONTENT_URI, QUERY_COLUMNS, SELECT_BY_ID,
261                 new String[] { uri.getLastPathSegment() }, null /* orderBy */);
262         assertThat(cursor.getCount()).isEqualTo(1);
263 
264         cursor.moveToNext();
265         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE)))
266                 .isEqualTo(GEO_SCOPE);
267         assertThat(cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.PLMN)))
268                 .isEqualTo(PLMN);
269         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.LAC))).isEqualTo(LAC);
270         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CID))).isEqualTo(CID);
271         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER)))
272                 .isEqualTo(SERIAL_NUMBER);
273         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY)))
274                 .isEqualTo(SERVICE_CATEGORY);
275         assertThat(cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE)))
276                 .isEqualTo(LANGUAGE_CODE);
277         assertThat(cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY)))
278                 .isEqualTo(MESSAGE_BODY);
279         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT)))
280                 .isEqualTo(MESSAGE_FORMAT);
281         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY)))
282                 .isEqualTo(MESSAGE_PRIORITY);
283         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.ETWS_WARNING_TYPE)))
284                 .isEqualTo(ETWS_WARNING_TYPE);
285         // TODO: Use system API CellBroadcasts.ETWS_IS_PRIMARY in S.
286         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow("etws_is_primary")))
287                 .isEqualTo(ETWS_IS_PRIMARY);
288         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_MESSAGE_CLASS)))
289                 .isEqualTo(CMAS_MESSAGE_CLASS);
290         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_CATEGORY)))
291                 .isEqualTo(CMAS_CATEGORY);
292         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_RESPONSE_TYPE)))
293                 .isEqualTo(CMAS_RESPONSE_TYPE);
294         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_SEVERITY)))
295                 .isEqualTo(CMAS_SEVERITY);
296         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_URGENCY)))
297                 .isEqualTo(CMAS_URGENCY);
298         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.CMAS_CERTAINTY)))
299                 .isEqualTo(CMAS_CERTAINTY);
300         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME)))
301                 .isEqualTo(RECEIVED_TIME);
302         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BROADCASTED)))
303                 .isEqualTo(MESSAGE_BROADCASTED);
304         assertThat(cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_DISPLAYED)))
305                 .isEqualTo(MESSAGE_NOT_DISPLAYED);
306         assertThat(cursor.getString(cursor.getColumnIndexOrThrow(
307                 CellBroadcasts.GEOMETRIES))).isEqualTo(GEOMETRIES_COORDINATES);
308     }
309 
310     /**
311      * This is used to give the CellBroadcastProviderTest a mocked context which takes a
312      * CellBroadcastProvider and attaches it to the ContentResolver.
313      */
314     private class MockContextWithProvider extends MockContext {
315         private final MockContentResolver mResolver;
316 
MockContextWithProvider(CellBroadcastProviderTestable cellBroadcastProvider)317         MockContextWithProvider(CellBroadcastProviderTestable cellBroadcastProvider) {
318             mResolver = new MockContentResolver();
319             cellBroadcastProvider.initializeForTesting(this);
320 
321             // Add given cellBroadcastProvider to mResolver, so that mResolver can send queries
322             // to the provider.
323             mResolver.addProvider(CellBroadcastProvider.AUTHORITY, cellBroadcastProvider);
324         }
325 
326         @Override
getContentResolver()327         public MockContentResolver getContentResolver() {
328             return mResolver;
329         }
330 
331 
332         @Override
getSystemService(String name)333         public Object getSystemService(String name) {
334             Log.d(TAG, "getSystemService: returning null");
335             return null;
336         }
337 
338         @Override
checkCallingOrSelfPermission(String permission)339         public int checkCallingOrSelfPermission(String permission) {
340             return PackageManager.PERMISSION_GRANTED;
341         }
342     }
343 
fakeCellBroadcast()344     private static ContentValues fakeCellBroadcast() {
345         ContentValues cv = new ContentValues();
346         cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, GEO_SCOPE);
347         cv.put(CellBroadcasts.PLMN, PLMN);
348         cv.put(CellBroadcasts.LAC, LAC);
349         cv.put(CellBroadcasts.CID, CID);
350         cv.put(CellBroadcasts.SERIAL_NUMBER, SERIAL_NUMBER);
351         cv.put(CellBroadcasts.SERVICE_CATEGORY, SERVICE_CATEGORY);
352         cv.put(CellBroadcasts.LANGUAGE_CODE, LANGUAGE_CODE);
353         cv.put(CellBroadcasts.MESSAGE_BODY, MESSAGE_BODY);
354         cv.put(CellBroadcasts.MESSAGE_FORMAT, MESSAGE_FORMAT);
355         cv.put(CellBroadcasts.MESSAGE_PRIORITY, MESSAGE_PRIORITY);
356         cv.put(CellBroadcasts.ETWS_WARNING_TYPE, ETWS_WARNING_TYPE);
357         // TODO: Use system API CellBroadcasts.ETWS_IS_PRIMARY in S.
358         cv.put("etws_is_primary", ETWS_IS_PRIMARY);
359         cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, CMAS_MESSAGE_CLASS);
360         cv.put(CellBroadcasts.CMAS_CATEGORY, CMAS_CATEGORY);
361         cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, CMAS_RESPONSE_TYPE);
362         cv.put(CellBroadcasts.CMAS_SEVERITY, CMAS_SEVERITY);
363         cv.put(CellBroadcasts.CMAS_URGENCY, CMAS_URGENCY);
364         cv.put(CellBroadcasts.CMAS_CERTAINTY, CMAS_CERTAINTY);
365         cv.put(CellBroadcasts.RECEIVED_TIME, RECEIVED_TIME);
366         cv.put(CellBroadcasts.MESSAGE_BROADCASTED, MESSAGE_BROADCASTED);
367         cv.put(CellBroadcasts.MESSAGE_DISPLAYED, MESSAGE_NOT_DISPLAYED);
368         cv.put(CellBroadcasts.GEOMETRIES, GEOMETRIES_COORDINATES);
369         return cv;
370     }
371 }
372