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.content.pm.PackageManager.PERMISSION_GRANTED; 20 import static android.provider.Telephony.ServiceStateTable; 21 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI; 22 import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE; 23 import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE; 24 import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE; 25 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION; 26 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE; 27 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId; 28 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField; 29 30 import android.Manifest; 31 import android.content.ContentProvider; 32 import android.content.ContentValues; 33 import android.content.Context; 34 import android.database.Cursor; 35 import android.database.MatrixCursor; 36 import android.database.MatrixCursor.RowBuilder; 37 import android.net.Uri; 38 import android.os.Binder; 39 import android.os.Build; 40 import android.os.Parcel; 41 import android.telephony.LocationAccessPolicy; 42 import android.telephony.ServiceState; 43 import android.telephony.SubscriptionManager; 44 import android.telephony.TelephonyManager; 45 import android.util.Log; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.telephony.TelephonyPermissions; 49 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Objects; 53 import java.util.Set; 54 55 /** 56 * The class to provide base facility to access ServiceState related content, 57 * which is stored in a SQLite database. 58 */ 59 public class ServiceStateProvider extends ContentProvider { 60 private static final String TAG = "ServiceStateProvider"; 61 62 public static final String AUTHORITY = ServiceStateTable.AUTHORITY; 63 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); 64 65 /** 66 * The current service state. 67 * 68 * This is the entire {@link ServiceState} object in byte array. 69 * 70 * @hide 71 */ 72 public static final String SERVICE_STATE = "service_state"; 73 74 /** 75 * An integer value indicating the current voice roaming type. 76 * <p> 77 * This is the same as {@link ServiceState#getVoiceRoamingType()}. 78 * @hide 79 */ 80 public static final String VOICE_ROAMING_TYPE = "voice_roaming_type"; 81 82 /** 83 * An integer value indicating the current data roaming type. 84 * <p> 85 * This is the same as {@link ServiceState#getDataRoamingType()}. 86 * @hide 87 */ 88 public static final String DATA_ROAMING_TYPE = "data_roaming_type"; 89 90 /** 91 * The current registered voice network operator name in long alphanumeric format. 92 * <p> 93 * This is the same as {@link ServiceState#getOperatorAlphaLong()}. 94 * @hide 95 */ 96 public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long"; 97 98 /** 99 * The current registered operator name in short alphanumeric format. 100 * <p> 101 * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice 102 * network operator name in long alphanumeric format. 103 * <p> 104 * This is the same as {@link ServiceState#getOperatorAlphaShort()}. 105 * @hide 106 */ 107 public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short"; 108 109 /** 110 * The current registered operator numeric id. 111 * <p> 112 * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit 113 * network code. 114 * <p> 115 * This is the same as {@link ServiceState#getOperatorNumeric()}. 116 */ 117 public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; 118 119 /** 120 * The current registered data network operator name in long alphanumeric format. 121 * <p> 122 * This is the same as {@link ServiceState#getOperatorAlphaLong()}. 123 * @hide 124 */ 125 public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long"; 126 127 /** 128 * The current registered data network operator name in short alphanumeric format. 129 * <p> 130 * This is the same as {@link ServiceState#getOperatorAlphaShort()}. 131 * @hide 132 */ 133 public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short"; 134 135 /** 136 * The current registered data network operator numeric id. 137 * <p> 138 * This is the same as {@link ServiceState#getOperatorNumeric()}. 139 * @hide 140 */ 141 public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric"; 142 143 /** 144 * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}. 145 * @hide 146 */ 147 public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology"; 148 149 /** 150 * This is the same as {@link ServiceState#getRilDataRadioTechnology()}. 151 * @hide 152 */ 153 public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology"; 154 155 /** 156 * This is the same as {@link ServiceState#getCssIndicator()}. 157 * @hide 158 */ 159 public static final String CSS_INDICATOR = "css_indicator"; 160 161 /** 162 * This is the same as {@link ServiceState#getCdmaNetworkId()}. 163 * @hide 164 */ 165 public static final String NETWORK_ID = "network_id"; 166 167 /** 168 * This is the same as {@link ServiceState#getCdmaSystemId()}. 169 * @hide 170 */ 171 public static final String SYSTEM_ID = "system_id"; 172 173 /** 174 * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}. 175 * @hide 176 */ 177 public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator"; 178 179 /** 180 * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}. 181 * @hide 182 */ 183 public static final String CDMA_DEFAULT_ROAMING_INDICATOR = 184 "cdma_default_roaming_indicator"; 185 186 /** 187 * This is the same as {@link ServiceState#getCdmaEriIconIndex()}. 188 * @hide 189 */ 190 public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index"; 191 192 /** 193 * This is the same as {@link ServiceState#getCdmaEriIconMode()}. 194 * @hide 195 */ 196 public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode"; 197 198 /** 199 * This is the same as {@link ServiceState#isEmergencyOnly()}. 200 * @hide 201 */ 202 public static final String IS_EMERGENCY_ONLY = "is_emergency_only"; 203 204 /** 205 * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}. 206 * @hide 207 */ 208 public static final String IS_DATA_ROAMING_FROM_REGISTRATION = 209 "is_data_roaming_from_registration"; 210 211 /** 212 * This is the same as {@link ServiceState#isUsingCarrierAggregation()}. 213 * @hide 214 */ 215 public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation"; 216 217 /** 218 * The current registered raw data network operator name in long alphanumeric format. 219 * <p> 220 * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}. 221 * @hide 222 */ 223 public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw"; 224 225 /** 226 * The current registered raw data network operator name in short alphanumeric format. 227 * <p> 228 * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}. 229 * @hide 230 */ 231 public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw"; 232 233 private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>(); 234 235 @VisibleForTesting 236 /* package */ static final String[] ALL_COLUMNS = { 237 VOICE_REG_STATE, 238 DATA_REG_STATE, 239 VOICE_ROAMING_TYPE, 240 DATA_ROAMING_TYPE, 241 VOICE_OPERATOR_ALPHA_LONG, 242 VOICE_OPERATOR_ALPHA_SHORT, 243 VOICE_OPERATOR_NUMERIC, 244 DATA_OPERATOR_ALPHA_LONG, 245 DATA_OPERATOR_ALPHA_SHORT, 246 DATA_OPERATOR_NUMERIC, 247 IS_MANUAL_NETWORK_SELECTION, 248 RIL_VOICE_RADIO_TECHNOLOGY, 249 RIL_DATA_RADIO_TECHNOLOGY, 250 CSS_INDICATOR, 251 NETWORK_ID, 252 SYSTEM_ID, 253 CDMA_ROAMING_INDICATOR, 254 CDMA_DEFAULT_ROAMING_INDICATOR, 255 CDMA_ERI_ICON_INDEX, 256 CDMA_ERI_ICON_MODE, 257 IS_EMERGENCY_ONLY, 258 IS_USING_CARRIER_AGGREGATION, 259 OPERATOR_ALPHA_LONG_RAW, 260 OPERATOR_ALPHA_SHORT_RAW, 261 DATA_NETWORK_TYPE, 262 DUPLEX_MODE, 263 }; 264 265 /** 266 * Columns that are exposed to public surface. 267 * These are the columns accessible to apps target S+ and lack 268 * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} permission. 269 */ 270 @VisibleForTesting 271 /* package */ static final String[] PUBLIC_COLUMNS = { 272 VOICE_REG_STATE, 273 DATA_REG_STATE, 274 VOICE_OPERATOR_NUMERIC, 275 IS_MANUAL_NETWORK_SELECTION, 276 DATA_NETWORK_TYPE, 277 DUPLEX_MODE 278 }; 279 280 /** 281 * Columns protected by location permissions (either FINE or COARSE). 282 * SecurityException will throw if applications without location permissions try to put those 283 * columns explicitly into cursor (e.g. through {@code projection} parameter in 284 * {@link #query(Uri, String[], String, String[], String)} method). 285 * Default (scrub-out) value will return if applications try to put all columns into cursor by 286 * specifying null of {@code projection} parameter and get values through the returned cursor. 287 */ 288 private static final Set<String> LOCATION_PROTECTED_COLUMNS_SET = Set.of( 289 NETWORK_ID, 290 SYSTEM_ID 291 ); 292 293 @Override onCreate()294 public boolean onCreate() { 295 return true; 296 } 297 298 /** 299 * Returns the {@link ServiceState} information on specified subscription. 300 * 301 * @param subId whose subscriber id is returned 302 * @return the {@link ServiceState} information on specified subscription. 303 */ 304 @VisibleForTesting getServiceState(int subId)305 public ServiceState getServiceState(int subId) { 306 return mServiceStates.get(subId); 307 } 308 309 /** 310 * Returns the system's default subscription id. 311 * 312 * @return the "system" default subscription id. 313 */ 314 @VisibleForTesting getDefaultSubId()315 public int getDefaultSubId() { 316 return SubscriptionManager.getDefaultSubscriptionId(); 317 } 318 319 @Override insert(Uri uri, ContentValues values)320 public Uri insert(Uri uri, ContentValues values) { 321 if (isPathPrefixMatch(uri, CONTENT_URI)) { 322 // Parse the subId 323 int subId = 0; 324 try { 325 subId = Integer.parseInt(uri.getLastPathSegment()); 326 } catch (NumberFormatException e) { 327 Log.e(TAG, "insert: no subId provided in uri"); 328 throw e; 329 } 330 Log.d(TAG, "subId=" + subId); 331 332 // handle DEFAULT_SUBSCRIPTION_ID 333 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 334 subId = getDefaultSubId(); 335 } 336 337 final Parcel p = Parcel.obtain(); 338 final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE); 339 p.unmarshall(rawBytes, 0, rawBytes.length); 340 p.setDataPosition(0); 341 342 // create the new service state 343 final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p); 344 345 // notify listeners 346 // if ss is null (e.g. first service state update) we will notify for all fields 347 ServiceState ss = getServiceState(subId); 348 notifyChangeForSubIdAndField(getContext(), ss, newSS, subId); 349 notifyChangeForSubId(getContext(), ss, newSS, subId); 350 351 // store the new service state 352 mServiceStates.put(subId, newSS); 353 return uri; 354 } 355 return null; 356 } 357 358 @Override delete(Uri uri, String selection, String[] selectionArgs)359 public int delete(Uri uri, String selection, String[] selectionArgs) { 360 throw new RuntimeException("Not supported"); 361 } 362 363 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)364 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 365 throw new RuntimeException("Not supported"); 366 } 367 368 @Override getType(Uri uri)369 public String getType(Uri uri) { 370 throw new RuntimeException("Not supported"); 371 } 372 373 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)374 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 375 String sortOrder) { 376 if (!isPathPrefixMatch(uri, CONTENT_URI)) { 377 throw new IllegalArgumentException("Invalid URI: " + uri); 378 } else { 379 // Parse the subId 380 int subId = 0; 381 try { 382 subId = Integer.parseInt(uri.getLastPathSegment()); 383 } catch (NumberFormatException e) { 384 Log.d(TAG, "query: no subId provided in uri, using default."); 385 subId = getDefaultSubId(); 386 } 387 Log.d(TAG, "subId=" + subId); 388 389 // handle DEFAULT_SUBSCRIPTION_ID 390 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 391 subId = getDefaultSubId(); 392 } 393 394 // Get the service state 395 ServiceState unredactedServiceState = getServiceState(subId); 396 if (unredactedServiceState == null) { 397 Log.d(TAG, "returning null"); 398 return null; 399 } 400 401 // TODO(b/182384053): replace targetSdk check with CompatChanges#isChangeEnabled 402 final boolean targetingAtLeastS = TelephonyPermissions.getTargetSdk(getContext(), 403 getCallingPackage()) >= Build.VERSION_CODES.S; 404 final boolean canReadPrivilegedPhoneState = getContext().checkCallingOrSelfPermission( 405 Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PERMISSION_GRANTED; 406 407 final String[] availableColumns; 408 final ServiceState ss; 409 if (targetingAtLeastS && !canReadPrivilegedPhoneState) { 410 // targetSdkVersion S+ without read privileged phone state permission can only 411 // access public columns which have no location sensitive info. 412 availableColumns = PUBLIC_COLUMNS; 413 ss = unredactedServiceState; 414 } else { 415 availableColumns = ALL_COLUMNS; 416 417 final boolean hasLocationPermission = hasLocationPermission(); 418 if (hasLocationPermission) { 419 // No matter the targetSdkVersion, return unredacted ServiceState if caller does 420 // have location permission. 421 ss = unredactedServiceState; 422 } else { 423 // The caller has targetSdkVersion S+ but no location permission. It explicitly 424 // requires location protected columns. Throw SecurityException to fail loudly. 425 if (targetingAtLeastS && projection != null) { 426 for (String requiredColumn : projection) { 427 if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) { 428 throw new SecurityException("Column " + requiredColumn 429 + "requires location permissions to access."); 430 } 431 } 432 } 433 434 // In all other cases, return the redacted ServiceState. 435 // The caller has no location permission but only requires columns without 436 // location sensitive info or "all" columns, return result that scrub out all 437 // sensitive info. In later case, we will not know which columns will be fetched 438 // from the returned cursor until the result has been returned. 439 ss = getLocationRedactedServiceState(unredactedServiceState); 440 } 441 } 442 443 // Build the result 444 final int voice_reg_state = ss.getState(); 445 final int data_reg_state = ss.getDataRegistrationState(); 446 final int voice_roaming_type = ss.getVoiceRoamingType(); 447 final int data_roaming_type = ss.getDataRoamingType(); 448 final String voice_operator_alpha_long = ss.getOperatorAlphaLong(); 449 final String voice_operator_alpha_short = ss.getOperatorAlphaShort(); 450 final String voice_operator_numeric = ss.getOperatorNumeric(); 451 final String data_operator_alpha_long = ss.getOperatorAlphaLong(); 452 final String data_operator_alpha_short = ss.getOperatorAlphaShort(); 453 final String data_operator_numeric = ss.getOperatorNumeric(); 454 final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0; 455 final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology(); 456 final int ril_data_radio_technology = ss.getRilDataRadioTechnology(); 457 final int css_indicator = ss.getCssIndicator(); 458 final int network_id = ss.getCdmaNetworkId(); 459 final int system_id = ss.getCdmaSystemId(); 460 final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator(); 461 final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator(); 462 final int cdma_eri_icon_index = ss.getCdmaEriIconIndex(); 463 final int cdma_eri_icon_mode = ss.getCdmaEriIconMode(); 464 final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0; 465 final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0; 466 final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw(); 467 final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw(); 468 final int data_network_type = ss.getDataNetworkType(); 469 final int duplex_mode = ss.getDuplexMode(); 470 471 Object[] data = availableColumns == ALL_COLUMNS ? new Object[]{ 472 // data for all columns 473 voice_reg_state, 474 data_reg_state, 475 voice_roaming_type, 476 data_roaming_type, 477 voice_operator_alpha_long, 478 voice_operator_alpha_short, 479 voice_operator_numeric, 480 data_operator_alpha_long, 481 data_operator_alpha_short, 482 data_operator_numeric, 483 is_manual_network_selection, 484 ril_voice_radio_technology, 485 ril_data_radio_technology, 486 css_indicator, 487 network_id, 488 system_id, 489 cdma_roaming_indicator, 490 cdma_default_roaming_indicator, 491 cdma_eri_icon_index, 492 cdma_eri_icon_mode, 493 is_emergency_only, 494 is_using_carrier_aggregation, 495 operator_alpha_long_raw, 496 operator_alpha_short_raw, 497 data_network_type, 498 duplex_mode, 499 } : new Object[]{ 500 // data for public columns only 501 voice_reg_state, 502 data_reg_state, 503 voice_operator_numeric, 504 is_manual_network_selection, 505 data_network_type, 506 duplex_mode, 507 }; 508 509 return buildSingleRowResult(projection, availableColumns, data); 510 } 511 } 512 buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)513 private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns, 514 Object[] data) { 515 if (projection == null) { 516 projection = availableColumns; 517 } 518 final MatrixCursor c = new MatrixCursor(projection, 1); 519 final RowBuilder row = c.newRow(); 520 for (int i = 0; i < c.getColumnCount(); i++) { 521 final String columnName = c.getColumnName(i); 522 boolean found = false; 523 for (int j = 0; j < availableColumns.length; j++) { 524 if (availableColumns[j].equals(columnName)) { 525 row.add(data[j]); 526 found = true; 527 break; 528 } 529 } 530 if (!found) { 531 throw new IllegalArgumentException("Invalid column " + projection[i]); 532 } 533 } 534 return c; 535 } 536 537 /** 538 * Notify interested apps that certain fields of the ServiceState have changed. 539 * 540 * Apps which want to wake when specific fields change can use 541 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit 542 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O. 543 * 544 * We will only notify for certain fields. This is an intentional change from the behavior of 545 * the broadcast. Listeners will be notified when the voice or data registration state or 546 * roaming type changes. 547 */ 548 @VisibleForTesting notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)549 public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS, 550 ServiceState newSS, int subId) { 551 final boolean firstUpdate = (oldSS == null) ? true : false; 552 553 // for every field, if the field has changed values, notify via the provider 554 if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) { 555 context.getContentResolver().notifyChange( 556 getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE), 557 /* observer= */ null, /* syncToNetwork= */ false); 558 } 559 if (firstUpdate || dataRegStateChanged(oldSS, newSS)) { 560 context.getContentResolver().notifyChange( 561 getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false); 562 } 563 if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) { 564 context.getContentResolver().notifyChange( 565 getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false); 566 } 567 if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) { 568 context.getContentResolver().notifyChange( 569 getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false); 570 } 571 if (firstUpdate || dataNetworkTypeChanged(oldSS, newSS)) { 572 context.getContentResolver().notifyChange( 573 getUriForSubscriptionIdAndField(subId, DATA_NETWORK_TYPE), null, false); 574 } 575 } 576 voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)577 private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) { 578 return oldSS.getState() != newSS.getState(); 579 } 580 dataRegStateChanged(ServiceState oldSS, ServiceState newSS)581 private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) { 582 return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState(); 583 } 584 voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)585 private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) { 586 return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType(); 587 } 588 dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)589 private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) { 590 return oldSS.getDataRoamingType() != newSS.getDataRoamingType(); 591 } 592 dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS)593 private static boolean dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS) { 594 return oldSS.getDataNetworkType() != newSS.getDataNetworkType(); 595 } 596 597 /** 598 * Notify interested apps that the ServiceState has changed. 599 * 600 * Apps which want to wake when any field in the ServiceState has changed can use 601 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit 602 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O. 603 * 604 * We will only notify for certain fields. This is an intentional change from the behavior of 605 * the broadcast. Listeners will only be notified when the voice/data registration state or 606 * roaming type changes. 607 */ 608 @VisibleForTesting notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)609 public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, 610 int subId) { 611 // if the voice or data registration or roaming state field has changed values, notify via 612 // the provider. 613 // If oldSS is null and newSS is not (e.g. first update of service state) this will also 614 // notify 615 if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS) 616 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS) 617 || dataNetworkTypeChanged(oldSS, newSS)) { 618 context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false); 619 } 620 } 621 622 /** 623 * Test if this is a path prefix match against the given Uri. Verifies that 624 * scheme, authority, and atomic path segments match. 625 * 626 * Copied from frameworks/base/core/java/android/net/Uri.java 627 */ isPathPrefixMatch(Uri uriA, Uri uriB)628 private boolean isPathPrefixMatch(Uri uriA, Uri uriB) { 629 if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false; 630 if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false; 631 632 List<String> segA = uriA.getPathSegments(); 633 List<String> segB = uriB.getPathSegments(); 634 635 final int size = segB.size(); 636 if (segA.size() < size) return false; 637 638 for (int i = 0; i < size; i++) { 639 if (!Objects.equals(segA.get(i), segB.get(i))) { 640 return false; 641 } 642 } 643 644 return true; 645 } 646 647 /** 648 * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance. 649 * 650 * @param state the ServiceState to convert into ContentValues 651 * @return the convertedContentValues instance 652 * @hide 653 */ getContentValuesForServiceState(ServiceState state)654 public static ContentValues getContentValuesForServiceState(ServiceState state) { 655 ContentValues values = new ContentValues(); 656 final Parcel p = Parcel.obtain(); 657 state.writeToParcel(p, 0); 658 // Turn the parcel to byte array. Safe to do this because the content values were never 659 // written into a persistent storage. ServiceStateProvider keeps values in the memory. 660 values.put(SERVICE_STATE, p.marshall()); 661 return values; 662 } 663 664 /** 665 * Check location permission with same policy as {@link TelephonyManager#getServiceState()} 666 * which enforces location permission check starting from Q. 667 */ hasLocationPermission()668 private boolean hasLocationPermission() { 669 LocationAccessPolicy.LocationPermissionResult locationPermissionResult = 670 LocationAccessPolicy.checkLocationPermission(getContext(), 671 new LocationAccessPolicy.LocationPermissionQuery.Builder() 672 .setCallingPackage(getCallingPackage()) 673 .setCallingFeatureId(getCallingAttributionTag()) 674 .setCallingPid(Binder.getCallingPid()) 675 .setCallingUid(Binder.getCallingUid()) 676 .setMethod("ServiceStateProvider#query") 677 .setLogAsInfo(true) 678 .setMinSdkVersionForFine(Build.VERSION_CODES.Q) 679 .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q) 680 .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q) 681 .build()); 682 return locationPermissionResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; 683 } 684 685 // Return a copy of ServiceState with all sensitive info redacted. 686 @VisibleForTesting getLocationRedactedServiceState(ServiceState serviceState)687 /* package */ static ServiceState getLocationRedactedServiceState(ServiceState serviceState) { 688 ServiceState ss = 689 serviceState.createLocationInfoSanitizedCopy(true /*removeCoarseLocation*/); 690 // TODO(b/188061647): remove the additional redaction once it is fixed in SS 691 ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID, ServiceState.UNKNOWN_ID); 692 return ss; 693 } 694 } 695