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