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 package android.carrierapi.cts; 17 18 import static com.google.common.truth.Truth.assertWithMessage; 19 20 import static org.junit.Assert.assertThrows; 21 import static org.junit.Assert.fail; 22 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.database.Cursor; 26 import android.database.sqlite.SQLiteException; 27 import android.net.Uri; 28 import android.provider.Telephony.Carriers; 29 import android.util.Log; 30 31 import androidx.test.runner.AndroidJUnit4; 32 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.util.Arrays; 38 import java.util.HashMap; 39 import java.util.Map; 40 41 /** 42 * Unit tests for the APN database exposed by {@link Carriers}. 43 * 44 * <p>Test using `atest CtsCarrierApiTestCases:ApnDatabaseTest` or `make cts -j64 && cts-tradefed 45 * run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.ApnDatabaseTest` 46 */ 47 @RunWith(AndroidJUnit4.class) 48 public class ApnDatabaseTest extends BaseCarrierApiTest { 49 private static final String TAG = "ApnDatabaseTest"; 50 51 private ContentResolver mContentResolver; 52 53 private static final String NAME = "carrierName"; 54 private static final String APN = "apn"; 55 private static final String PROXY = "proxy"; 56 private static final String PORT = "port"; 57 private static final String MMSC = "mmsc"; 58 private static final String MMSPROXY = "mmsproxy"; 59 private static final String MMSPORT = "mmsport"; 60 private static final String NUMERIC = "numeric"; 61 private static final String USER = "user"; 62 private static final String PASSWORD = "password"; 63 private static final String AUTH_TYPE = "auth_type"; 64 private static final String TYPE = "type"; 65 private static final String PROTOCOL = "protocol"; 66 private static final String ROAMING_PROTOCOL = "roaming_protocol"; 67 private static final String CARRIER_ENABLED = "true"; 68 private static final String NETWORK_TYPE_BITMASK = "0"; 69 private static final String BEARER = "0"; 70 71 private static final Map<String, String> APN_MAP = 72 new HashMap<String, String>() { 73 { 74 put(Carriers.NAME, NAME); 75 put(Carriers.APN, APN); 76 put(Carriers.PROXY, PROXY); 77 put(Carriers.PORT, PORT); 78 put(Carriers.MMSC, MMSC); 79 put(Carriers.MMSPROXY, MMSPROXY); 80 put(Carriers.MMSPORT, MMSPORT); 81 put(Carriers.NUMERIC, NUMERIC); 82 put(Carriers.USER, USER); 83 put(Carriers.PASSWORD, PASSWORD); 84 put(Carriers.AUTH_TYPE, AUTH_TYPE); 85 put(Carriers.TYPE, TYPE); 86 put(Carriers.PROTOCOL, PROTOCOL); 87 put(Carriers.ROAMING_PROTOCOL, ROAMING_PROTOCOL); 88 put(Carriers.CARRIER_ENABLED, CARRIER_ENABLED); 89 put(Carriers.NETWORK_TYPE_BITMASK, NETWORK_TYPE_BITMASK); 90 put(Carriers.BEARER, BEARER); 91 } 92 }; 93 94 // Faked network type bitmask and its compatible bearer bitmask. 95 private static final int NETWORK_TYPE_BITMASK_NUMBER = 1 << (13 - 1); 96 private static final int RIL_RADIO_TECHNOLOGY_BITMASK_NUMBER = 1 << (14 - 1); 97 98 @Before setUp()99 public void setUp() throws Exception { 100 mContentResolver = getContext().getContentResolver(); 101 } 102 103 /** 104 * Test inserting, querying, updating and deleting values in carriers table. Verify that the 105 * inserted values match the result of the query and are deleted. 106 */ 107 @Test testValidCase()108 public void testValidCase() { 109 Uri uri = Carriers.CONTENT_URI; 110 // Create A set of column_name/value pairs to add to the database. 111 ContentValues contentValues = makeDefaultContentValues(); 112 113 try { 114 // Insert the value into database. 115 Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString()); 116 Uri newUri = mContentResolver.insert(uri, contentValues); 117 assertWithMessage("Failed to insert to table").that(newUri).isNotNull(); 118 119 // Get the values in table. 120 final String selection = Carriers.NUMERIC + "=?"; 121 String[] selectionArgs = {NUMERIC}; 122 String[] apnProjection = APN_MAP.keySet().toArray(new String[APN_MAP.size()]); 123 Log.d( 124 TAG, 125 "testInsertCarriers query projection: " 126 + Arrays.toString(apnProjection) 127 + "\ntestInsertCarriers selection: " 128 + selection 129 + "\ntestInsertCarriers selectionArgs: " 130 + Arrays.toString(selectionArgs)); 131 Cursor cursor = 132 mContentResolver.query(uri, apnProjection, selection, selectionArgs, null); 133 134 // Verify that the inserted value match the results of the query 135 assertWithMessage("Failed to query the table").that(cursor).isNotNull(); 136 assertWithMessage("Unexpected number of APNs returned by cursor") 137 .that(cursor.getCount()) 138 .isEqualTo(1); 139 cursor.moveToFirst(); 140 for (Map.Entry<String, String> entry : APN_MAP.entrySet()) { 141 assertWithMessage("Unexpected value returned by cursor") 142 .that(cursor.getString(cursor.getColumnIndex(entry.getKey()))) 143 .isEqualTo(entry.getValue()); 144 } 145 146 // update the apn 147 final String newApn = "newapn"; 148 Log.d(TAG, "Update the APN field to: " + newApn); 149 contentValues.put(Carriers.APN, newApn); 150 final int updateCount = 151 mContentResolver.update(uri, contentValues, selection, selectionArgs); 152 assertWithMessage("Unexpected number of rows updated").that(updateCount).isEqualTo(1); 153 154 // Verify the updated value 155 cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null); 156 assertWithMessage("Failed to query the table").that(cursor).isNotNull(); 157 assertWithMessage("Unexpected number of APNs returned by cursor") 158 .that(cursor.getCount()) 159 .isEqualTo(1); 160 cursor.moveToFirst(); 161 assertWithMessage("Unexpected value returned by cursor") 162 .that(cursor.getString(cursor.getColumnIndex(Carriers.APN))) 163 .isEqualTo(newApn); 164 165 // delete test content 166 final String selectionToDelete = Carriers.NUMERIC + "=?"; 167 String[] selectionArgsToDelete = {NUMERIC}; 168 Log.d( 169 TAG, 170 "testInsertCarriers deleting selection: " 171 + selectionToDelete 172 + "testInsertCarriers selectionArgs: " 173 + Arrays.toString(selectionArgs)); 174 int numRowsDeleted = 175 mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete); 176 assertWithMessage("Unexpected number of rows deleted") 177 .that(numRowsDeleted) 178 .isEqualTo(1); 179 180 // verify that deleted values are gone 181 cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null); 182 assertWithMessage("Unexpected number of rows deleted") 183 .that(cursor.getCount()) 184 .isEqualTo(0); 185 } catch (SecurityException e) { 186 fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE); 187 } 188 } 189 190 @Test testQueryConflictCase()191 public void testQueryConflictCase() { 192 String invalidColumn = "random"; 193 Uri uri = Carriers.CONTENT_URI; 194 // Create a set of column_name/value pairs to add to the database. 195 ContentValues contentValues = new ContentValues(); 196 contentValues.put(Carriers.NAME, NAME); 197 contentValues.put(Carriers.APN, APN); 198 contentValues.put(Carriers.PORT, PORT); 199 contentValues.put(Carriers.PROTOCOL, PROTOCOL); 200 contentValues.put(Carriers.NUMERIC, NUMERIC); 201 202 try { 203 // Insert the value into database. 204 Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString()); 205 Uri newUri = mContentResolver.insert(uri, contentValues); 206 assertWithMessage("Failed to insert to table").that(newUri).isNotNull(); 207 208 // Try to get the value with invalid selection 209 final String[] testProjection = { 210 Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC, 211 }; 212 final String selection = invalidColumn + "=?"; 213 String[] selectionArgs = {invalidColumn}; 214 Log.d( 215 TAG, 216 "testInsertCarriers query projection: " 217 + Arrays.toString(testProjection) 218 + "\ntestInsertCarriers selection: " 219 + selection 220 + "\ntestInsertCarriers selectionArgs: " 221 + Arrays.toString(selectionArgs)); 222 Cursor cursor = 223 mContentResolver.query(uri, testProjection, selection, selectionArgs, null); 224 assertWithMessage("Failed to query the table").that(cursor).isNull(); 225 226 // delete test content 227 final String selectionToDelete = Carriers.NAME + "=?"; 228 String[] selectionArgsToDelete = {NAME}; 229 Log.d( 230 TAG, 231 "testInsertCarriers deleting selection: " 232 + selectionToDelete 233 + "testInsertCarriers selectionArgs: " 234 + Arrays.toString(selectionArgs)); 235 int numRowsDeleted = 236 mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete); 237 assertWithMessage("Unexpected number of rows deleted") 238 .that(numRowsDeleted) 239 .isEqualTo(1); 240 241 // verify that deleted values are gone 242 cursor = 243 mContentResolver.query( 244 uri, testProjection, selectionToDelete, selectionArgsToDelete, null); 245 assertWithMessage("Unexpected number of rows deleted") 246 .that(cursor.getCount()) 247 .isEqualTo(0); 248 } catch (SecurityException e) { 249 fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE); 250 } 251 } 252 253 @Test testUpdateConflictCase()254 public void testUpdateConflictCase() { 255 Uri uri = Carriers.CONTENT_URI; 256 // Create a set of column_name/value pairs to add to the database. 257 ContentValues contentValues = new ContentValues(); 258 contentValues.put(Carriers.NAME, NAME); 259 contentValues.put(Carriers.APN, APN); 260 contentValues.put(Carriers.PORT, PORT); 261 contentValues.put(Carriers.PROTOCOL, PROTOCOL); 262 contentValues.put(Carriers.NUMERIC, NUMERIC); 263 264 try { 265 // Insert the value into database. 266 Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString()); 267 Uri newUri = mContentResolver.insert(uri, contentValues); 268 assertWithMessage("Failed to insert to table").that(newUri).isNotNull(); 269 270 // Try to get the value with invalid selection 271 final String[] testProjection = { 272 Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC, 273 }; 274 String selection = Carriers.NAME + "=?"; 275 String[] selectionArgs = {NAME}; 276 Log.d( 277 TAG, 278 "testInsertCarriers query projection: " 279 + Arrays.toString(testProjection) 280 + "\ntestInsertCarriers selection: " 281 + selection 282 + "\ntestInsertCarriers selectionArgs: " 283 + Arrays.toString(selectionArgs)); 284 Cursor cursor = 285 mContentResolver.query(uri, testProjection, selection, selectionArgs, null); 286 assertWithMessage("Unexpected number of APNs returned by cursor") 287 .that(cursor.getCount()) 288 .isEqualTo(1); 289 290 // Update the table with invalid column 291 String invalidColumn = "random"; 292 contentValues.put(invalidColumn, invalidColumn); 293 // Expected: If there's no such a column, an exception will be thrown and 294 // ActivityManager will kill this process shortly. 295 assertThrows( 296 SQLiteException.class, 297 () -> mContentResolver.update(uri, contentValues, selection, selectionArgs)); 298 299 // delete test content 300 final String selectionToDelete = Carriers.NAME + "=?"; 301 String[] selectionArgsToDelete = {NAME}; 302 Log.d( 303 TAG, 304 "testInsertCarriers deleting selection: " 305 + selectionToDelete 306 + "testInsertCarriers selectionArgs: " 307 + Arrays.toString(selectionArgs)); 308 int numRowsDeleted = 309 mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete); 310 assertWithMessage("Unexpected number of rows deleted") 311 .that(numRowsDeleted) 312 .isEqualTo(1); 313 314 // verify that deleted values are gone 315 cursor = 316 mContentResolver.query( 317 uri, testProjection, selectionToDelete, selectionArgsToDelete, null); 318 assertWithMessage("Unexpected number of rows deleted") 319 .that(cursor.getCount()) 320 .isEqualTo(0); 321 } catch (SecurityException e) { 322 fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE); 323 } 324 } 325 326 @Test testDeleteConflictCase()327 public void testDeleteConflictCase() { 328 String invalidColumn = "random"; 329 Uri uri = Carriers.CONTENT_URI; 330 // Create a set of column_name/value pairs to add to the database. 331 ContentValues contentValues = new ContentValues(); 332 contentValues.put(Carriers.NAME, NAME); 333 contentValues.put(Carriers.APN, APN); 334 contentValues.put(Carriers.PORT, PORT); 335 contentValues.put(Carriers.PROTOCOL, PROTOCOL); 336 contentValues.put(Carriers.NUMERIC, NUMERIC); 337 338 try { 339 // Insert the value into database. 340 Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString()); 341 Uri newUri = mContentResolver.insert(uri, contentValues); 342 assertWithMessage("Failed to insert to table").that(newUri).isNotNull(); 343 344 // Get the values in table. 345 final String[] testProjection = { 346 Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC, 347 }; 348 String selection = Carriers.NAME + "=?"; 349 String[] selectionArgs = {NAME}; 350 Log.d( 351 TAG, 352 "testInsertCarriers query projection: " 353 + Arrays.toString(testProjection) 354 + "\ntestInsertCarriers selection: " 355 + selection 356 + "\ntestInsertCarriers selectionArgs: " 357 + Arrays.toString(selectionArgs)); 358 Cursor cursor = 359 mContentResolver.query(uri, testProjection, selection, selectionArgs, null); 360 assertWithMessage("Unexpected number of APNs returned by cursor") 361 .that(cursor.getCount()) 362 .isEqualTo(1); 363 364 // try to delete with invalid selection 365 String invalidSelectionToDelete = invalidColumn + "=?"; 366 String[] invalidSelectionArgsToDelete = {invalidColumn}; 367 Log.d( 368 TAG, 369 "testInsertCarriers deleting selection: " 370 + invalidSelectionToDelete 371 + "testInsertCarriers selectionArgs: " 372 + Arrays.toString(selectionArgs)); 373 374 // Expected: If there's no such a column, an exception will be thrown and 375 // ActivityManager will kill this process shortly. 376 assertThrows( 377 SQLiteException.class, 378 () -> 379 mContentResolver.delete( 380 uri, invalidSelectionToDelete, invalidSelectionArgsToDelete)); 381 382 // verify that deleted value is still there 383 selection = Carriers.NAME + "=?"; 384 selectionArgs[0] = NAME; 385 cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null); 386 assertWithMessage("Unexpected number of APNs returned by cursor") 387 .that(cursor.getCount()) 388 .isEqualTo(1); 389 390 // delete test content 391 String selectionToDelete = Carriers.NAME + "=?"; 392 String[] selectionArgsToDelete = {NAME}; 393 Log.d( 394 TAG, 395 "testInsertCarriers deleting selection: " 396 + selectionToDelete 397 + "testInsertCarriers selectionArgs: " 398 + Arrays.toString(selectionArgs)); 399 int numRowsDeleted = 400 mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete); 401 assertWithMessage("Unexpected number of rows deleted") 402 .that(numRowsDeleted) 403 .isEqualTo(1); 404 405 // verify that deleted values are gone 406 cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null); 407 assertWithMessage("Unexpected number of rows deleted") 408 .that(cursor.getCount()) 409 .isEqualTo(0); 410 } catch (SecurityException e) { 411 fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE); 412 } 413 } 414 makeDefaultContentValues()415 private ContentValues makeDefaultContentValues() { 416 ContentValues contentValues = new ContentValues(); 417 for (Map.Entry<String, String> entry : APN_MAP.entrySet()) { 418 contentValues.put(entry.getKey(), entry.getValue()); 419 } 420 return contentValues; 421 } 422 } 423