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