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