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.phone; 18 19 import static android.provider.Telephony.ServiceStateTable; 20 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI; 21 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION; 22 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE; 23 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId; 24 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField; 25 26 import android.content.ContentProvider; 27 import android.content.ContentValues; 28 import android.content.Context; 29 import android.database.Cursor; 30 import android.database.MatrixCursor; 31 import android.database.MatrixCursor.RowBuilder; 32 import android.net.Uri; 33 import android.os.Parcel; 34 import android.telephony.ServiceState; 35 import android.telephony.SubscriptionManager; 36 import android.util.Log; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Objects; 43 44 /** 45 * The class to provide base facility to access ServiceState related content, 46 * which is stored in a SQLite database. 47 */ 48 public class ServiceStateProvider extends ContentProvider { 49 private static final String TAG = "ServiceStateProvider"; 50 51 public static final String AUTHORITY = ServiceStateTable.AUTHORITY; 52 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); 53 54 /** 55 * The current service state. 56 * 57 * This is the entire {@link ServiceState} object in byte array. 58 * 59 * @hide 60 */ 61 public static final String SERVICE_STATE = "service_state"; 62 63 /** 64 * An integer value indicating the current data service state. 65 * <p> 66 * Valid values: {@link ServiceState#STATE_IN_SERVICE}, 67 * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY}, 68 * {@link ServiceState#STATE_POWER_OFF}. 69 * <p> 70 * This is the same as {@link ServiceState#getDataRegState()}. 71 * @hide 72 */ 73 public static final String DATA_REG_STATE = "data_reg_state"; 74 75 /** 76 * An integer value indicating the current voice roaming type. 77 * <p> 78 * This is the same as {@link ServiceState#getVoiceRoamingType()}. 79 * @hide 80 */ 81 public static final String VOICE_ROAMING_TYPE = "voice_roaming_type"; 82 83 /** 84 * An integer value indicating the current data roaming type. 85 * <p> 86 * This is the same as {@link ServiceState#getDataRoamingType()}. 87 * @hide 88 */ 89 public static final String DATA_ROAMING_TYPE = "data_roaming_type"; 90 91 /** 92 * The current registered voice network operator name in long alphanumeric format. 93 * <p> 94 * This is the same as {@link ServiceState#getOperatorAlphaLong()}. 95 * @hide 96 */ 97 public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long"; 98 99 /** 100 * The current registered operator name in short alphanumeric format. 101 * <p> 102 * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice 103 * network operator name in long alphanumeric format. 104 * <p> 105 * This is the same as {@link ServiceState#getOperatorAlphaShort()}. 106 * @hide 107 */ 108 public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short"; 109 110 /** 111 * The current registered operator numeric id. 112 * <p> 113 * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit 114 * network code. 115 * <p> 116 * This is the same as {@link ServiceState#getOperatorNumeric()}. 117 */ 118 public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; 119 120 /** 121 * The current registered data network operator name in long alphanumeric format. 122 * <p> 123 * This is the same as {@link ServiceState#getOperatorAlphaLong()}. 124 * @hide 125 */ 126 public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long"; 127 128 /** 129 * The current registered data network operator name in short alphanumeric format. 130 * <p> 131 * This is the same as {@link ServiceState#getOperatorAlphaShort()}. 132 * @hide 133 */ 134 public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short"; 135 136 /** 137 * The current registered data network operator numeric id. 138 * <p> 139 * This is the same as {@link ServiceState#getOperatorNumeric()}. 140 * @hide 141 */ 142 public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric"; 143 144 /** 145 * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}. 146 * @hide 147 */ 148 public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology"; 149 150 /** 151 * This is the same as {@link ServiceState#getRilDataRadioTechnology()}. 152 * @hide 153 */ 154 public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology"; 155 156 /** 157 * This is the same as {@link ServiceState#getCssIndicator()}. 158 * @hide 159 */ 160 public static final String CSS_INDICATOR = "css_indicator"; 161 162 /** 163 * This is the same as {@link ServiceState#getCdmaNetworkId()}. 164 * @hide 165 */ 166 public static final String NETWORK_ID = "network_id"; 167 168 /** 169 * This is the same as {@link ServiceState#getCdmaSystemId()}. 170 * @hide 171 */ 172 public static final String SYSTEM_ID = "system_id"; 173 174 /** 175 * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}. 176 * @hide 177 */ 178 public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator"; 179 180 /** 181 * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}. 182 * @hide 183 */ 184 public static final String CDMA_DEFAULT_ROAMING_INDICATOR = 185 "cdma_default_roaming_indicator"; 186 187 /** 188 * This is the same as {@link ServiceState#getCdmaEriIconIndex()}. 189 * @hide 190 */ 191 public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index"; 192 193 /** 194 * This is the same as {@link ServiceState#getCdmaEriIconMode()}. 195 * @hide 196 */ 197 public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode"; 198 199 /** 200 * This is the same as {@link ServiceState#isEmergencyOnly()}. 201 * @hide 202 */ 203 public static final String IS_EMERGENCY_ONLY = "is_emergency_only"; 204 205 /** 206 * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}. 207 * @hide 208 */ 209 public static final String IS_DATA_ROAMING_FROM_REGISTRATION = 210 "is_data_roaming_from_registration"; 211 212 /** 213 * This is the same as {@link ServiceState#isUsingCarrierAggregation()}. 214 * @hide 215 */ 216 public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation"; 217 218 /** 219 * The current registered raw data network operator name in long alphanumeric format. 220 * <p> 221 * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}. 222 * @hide 223 */ 224 public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw"; 225 226 /** 227 * The current registered raw data network operator name in short alphanumeric format. 228 * <p> 229 * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}. 230 * @hide 231 */ 232 public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw"; 233 234 private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>(); 235 private static final String[] sColumns = { 236 VOICE_REG_STATE, 237 DATA_REG_STATE, 238 VOICE_ROAMING_TYPE, 239 DATA_ROAMING_TYPE, 240 VOICE_OPERATOR_ALPHA_LONG, 241 VOICE_OPERATOR_ALPHA_SHORT, 242 VOICE_OPERATOR_NUMERIC, 243 DATA_OPERATOR_ALPHA_LONG, 244 DATA_OPERATOR_ALPHA_SHORT, 245 DATA_OPERATOR_NUMERIC, 246 IS_MANUAL_NETWORK_SELECTION, 247 RIL_VOICE_RADIO_TECHNOLOGY, 248 RIL_DATA_RADIO_TECHNOLOGY, 249 CSS_INDICATOR, 250 NETWORK_ID, 251 SYSTEM_ID, 252 CDMA_ROAMING_INDICATOR, 253 CDMA_DEFAULT_ROAMING_INDICATOR, 254 CDMA_ERI_ICON_INDEX, 255 CDMA_ERI_ICON_MODE, 256 IS_EMERGENCY_ONLY, 257 IS_USING_CARRIER_AGGREGATION, 258 OPERATOR_ALPHA_LONG_RAW, 259 OPERATOR_ALPHA_SHORT_RAW, 260 }; 261 262 @Override onCreate()263 public boolean onCreate() { 264 return true; 265 } 266 267 /** 268 * Returns the {@link ServiceState} information on specified subscription. 269 * 270 * @param subId whose subscriber id is returned 271 * @return the {@link ServiceState} information on specified subscription. 272 */ 273 @VisibleForTesting getServiceState(int subId)274 public ServiceState getServiceState(int subId) { 275 return mServiceStates.get(subId); 276 } 277 278 /** 279 * Returns the system's default subscription id. 280 * 281 * @return the "system" default subscription id. 282 */ 283 @VisibleForTesting getDefaultSubId()284 public int getDefaultSubId() { 285 return SubscriptionManager.getDefaultSubscriptionId(); 286 } 287 288 @Override insert(Uri uri, ContentValues values)289 public Uri insert(Uri uri, ContentValues values) { 290 if (isPathPrefixMatch(uri, CONTENT_URI)) { 291 // Parse the subId 292 int subId = 0; 293 try { 294 subId = Integer.parseInt(uri.getLastPathSegment()); 295 } catch (NumberFormatException e) { 296 Log.e(TAG, "insert: no subId provided in uri"); 297 throw e; 298 } 299 Log.d(TAG, "subId=" + subId); 300 301 // handle DEFAULT_SUBSCRIPTION_ID 302 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 303 subId = getDefaultSubId(); 304 } 305 306 final Parcel p = Parcel.obtain(); 307 final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE); 308 p.unmarshall(rawBytes, 0, rawBytes.length); 309 p.setDataPosition(0); 310 311 // create the new service state 312 final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p); 313 314 // notify listeners 315 // if ss is null (e.g. first service state update) we will notify for all fields 316 ServiceState ss = getServiceState(subId); 317 notifyChangeForSubIdAndField(getContext(), ss, newSS, subId); 318 notifyChangeForSubId(getContext(), ss, newSS, subId); 319 320 // store the new service state 321 mServiceStates.put(subId, newSS); 322 return uri; 323 } 324 return null; 325 } 326 327 @Override delete(Uri uri, String selection, String[] selectionArgs)328 public int delete(Uri uri, String selection, String[] selectionArgs) { 329 throw new RuntimeException("Not supported"); 330 } 331 332 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)333 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 334 throw new RuntimeException("Not supported"); 335 } 336 337 @Override getType(Uri uri)338 public String getType(Uri uri) { 339 throw new RuntimeException("Not supported"); 340 } 341 342 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)343 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 344 String sortOrder) { 345 if (!isPathPrefixMatch(uri, CONTENT_URI)) { 346 throw new IllegalArgumentException("Invalid URI: " + uri); 347 } else { 348 // Parse the subId 349 int subId = 0; 350 try { 351 subId = Integer.parseInt(uri.getLastPathSegment()); 352 } catch (NumberFormatException e) { 353 Log.d(TAG, "query: no subId provided in uri, using default."); 354 subId = getDefaultSubId(); 355 } 356 Log.d(TAG, "subId=" + subId); 357 358 // handle DEFAULT_SUBSCRIPTION_ID 359 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 360 subId = getDefaultSubId(); 361 } 362 363 // Get the service state 364 ServiceState ss = getServiceState(subId); 365 if (ss == null) { 366 Log.d(TAG, "returning null"); 367 return null; 368 } 369 370 // Build the result 371 final int voice_reg_state = ss.getState(); 372 final int data_reg_state = ss.getDataRegistrationState(); 373 final int voice_roaming_type = ss.getVoiceRoamingType(); 374 final int data_roaming_type = ss.getDataRoamingType(); 375 final String voice_operator_alpha_long = ss.getOperatorAlphaLong(); 376 final String voice_operator_alpha_short = ss.getOperatorAlphaShort(); 377 final String voice_operator_numeric = ss.getOperatorNumeric(); 378 final String data_operator_alpha_long = ss.getOperatorAlphaLong(); 379 final String data_operator_alpha_short = ss.getOperatorAlphaShort(); 380 final String data_operator_numeric = ss.getOperatorNumeric(); 381 final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0; 382 final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology(); 383 final int ril_data_radio_technology = ss.getRilDataRadioTechnology(); 384 final int css_indicator = ss.getCssIndicator(); 385 final int network_id = ss.getCdmaNetworkId(); 386 final int system_id = ss.getCdmaSystemId(); 387 final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator(); 388 final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator(); 389 final int cdma_eri_icon_index = ss.getCdmaEriIconIndex(); 390 final int cdma_eri_icon_mode = ss.getCdmaEriIconMode(); 391 final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0; 392 final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0; 393 final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw(); 394 final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw(); 395 396 return buildSingleRowResult(projection, sColumns, new Object[] { 397 voice_reg_state, 398 data_reg_state, 399 voice_roaming_type, 400 data_roaming_type, 401 voice_operator_alpha_long, 402 voice_operator_alpha_short, 403 voice_operator_numeric, 404 data_operator_alpha_long, 405 data_operator_alpha_short, 406 data_operator_numeric, 407 is_manual_network_selection, 408 ril_voice_radio_technology, 409 ril_data_radio_technology, 410 css_indicator, 411 network_id, 412 system_id, 413 cdma_roaming_indicator, 414 cdma_default_roaming_indicator, 415 cdma_eri_icon_index, 416 cdma_eri_icon_mode, 417 is_emergency_only, 418 is_using_carrier_aggregation, 419 operator_alpha_long_raw, 420 operator_alpha_short_raw, 421 }); 422 } 423 } 424 buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)425 private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns, 426 Object[] data) { 427 if (projection == null) { 428 projection = availableColumns; 429 } 430 final MatrixCursor c = new MatrixCursor(projection, 1); 431 final RowBuilder row = c.newRow(); 432 for (int i = 0; i < c.getColumnCount(); i++) { 433 final String columnName = c.getColumnName(i); 434 boolean found = false; 435 for (int j = 0; j < availableColumns.length; j++) { 436 if (availableColumns[j].equals(columnName)) { 437 row.add(data[j]); 438 found = true; 439 break; 440 } 441 } 442 if (!found) { 443 throw new IllegalArgumentException("Invalid column " + projection[i]); 444 } 445 } 446 return c; 447 } 448 449 /** 450 * Notify interested apps that certain fields of the ServiceState have changed. 451 * 452 * Apps which want to wake when specific fields change can use 453 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit 454 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O. 455 * 456 * We will only notify for certain fields. This is an intentional change from the behavior of 457 * the broadcast. Listeners will be notified when the voice or data registration state or 458 * roaming type changes. 459 */ 460 @VisibleForTesting notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)461 public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS, 462 ServiceState newSS, int subId) { 463 final boolean firstUpdate = (oldSS == null) ? true : false; 464 465 // for every field, if the field has changed values, notify via the provider 466 if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) { 467 context.getContentResolver().notifyChange( 468 getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE), 469 /* observer= */ null, /* syncToNetwork= */ false); 470 } 471 if (firstUpdate || dataRegStateChanged(oldSS, newSS)) { 472 context.getContentResolver().notifyChange( 473 getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false); 474 } 475 if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) { 476 context.getContentResolver().notifyChange( 477 getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false); 478 } 479 if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) { 480 context.getContentResolver().notifyChange( 481 getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false); 482 } 483 } 484 voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)485 private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) { 486 return oldSS.getState() != newSS.getState(); 487 } 488 dataRegStateChanged(ServiceState oldSS, ServiceState newSS)489 private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) { 490 return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState(); 491 } 492 voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)493 private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) { 494 return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType(); 495 } 496 dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)497 private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) { 498 return oldSS.getDataRoamingType() != newSS.getDataRoamingType(); 499 } 500 501 /** 502 * Notify interested apps that the ServiceState has changed. 503 * 504 * Apps which want to wake when any field in the ServiceState has changed can use 505 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit 506 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O. 507 * 508 * We will only notify for certain fields. This is an intentional change from the behavior of 509 * the broadcast. Listeners will only be notified when the voice/data registration state or 510 * roaming type changes. 511 */ 512 @VisibleForTesting notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)513 public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, 514 int subId) { 515 // if the voice or data registration or roaming state field has changed values, notify via 516 // the provider. 517 // If oldSS is null and newSS is not (e.g. first update of service state) this will also 518 // notify 519 if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS) 520 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) { 521 context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false); 522 } 523 } 524 525 /** 526 * Test if this is a path prefix match against the given Uri. Verifies that 527 * scheme, authority, and atomic path segments match. 528 * 529 * Copied from frameworks/base/core/java/android/net/Uri.java 530 */ isPathPrefixMatch(Uri uriA, Uri uriB)531 private boolean isPathPrefixMatch(Uri uriA, Uri uriB) { 532 if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false; 533 if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false; 534 535 List<String> segA = uriA.getPathSegments(); 536 List<String> segB = uriB.getPathSegments(); 537 538 final int size = segB.size(); 539 if (segA.size() < size) return false; 540 541 for (int i = 0; i < size; i++) { 542 if (!Objects.equals(segA.get(i), segB.get(i))) { 543 return false; 544 } 545 } 546 547 return true; 548 } 549 550 /** 551 * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance. 552 * 553 * @param state the ServiceState to convert into ContentValues 554 * @return the convertedContentValues instance 555 * @hide 556 */ getContentValuesForServiceState(ServiceState state)557 public static ContentValues getContentValuesForServiceState(ServiceState state) { 558 ContentValues values = new ContentValues(); 559 final Parcel p = Parcel.obtain(); 560 state.writeToParcel(p, 0); 561 // Turn the parcel to byte array. Safe to do this because the content values were never 562 // written into a persistent storage. ServiceStateProvider keeps values in the memory. 563 values.put(SERVICE_STATE, p.marshall()); 564 return values; 565 } 566 } 567