blob: 56786f9b84cad4a1ba8901d4e4d32e1dc259d856 [file] [log] [blame]
SongFerngWang1bb5a6f2019-12-10 00:42:54 +08001/*
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
17package com.android.phone;
18
Rambo Wang979ccae2021-04-28 17:51:36 -070019import static android.content.pm.PackageManager.PERMISSION_GRANTED;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080020import static android.provider.Telephony.ServiceStateTable;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080021import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
Rambo Wangfbdb3482021-03-15 21:14:19 -070022import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
Rambo Wang191a6812021-03-15 11:41:49 -070023import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
Rambo Wang917ee2f2021-03-15 12:15:51 -070024import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080025import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080026import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080027import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
28import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
29
Rambo Wang979ccae2021-04-28 17:51:36 -070030import android.Manifest;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080031import android.content.ContentProvider;
32import android.content.ContentValues;
33import android.content.Context;
34import android.database.Cursor;
35import android.database.MatrixCursor;
36import android.database.MatrixCursor.RowBuilder;
37import android.net.Uri;
Rambo Wang979ccae2021-04-28 17:51:36 -070038import android.os.Binder;
39import android.os.Build;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080040import android.os.Parcel;
Rambo Wang979ccae2021-04-28 17:51:36 -070041import android.telephony.LocationAccessPolicy;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080042import android.telephony.ServiceState;
43import android.telephony.SubscriptionManager;
Rambo Wang88c36272021-06-23 13:02:34 -070044import android.telephony.TelephonyManager;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080045import android.util.Log;
46
47import com.android.internal.annotations.VisibleForTesting;
Rambo Wang979ccae2021-04-28 17:51:36 -070048import com.android.internal.telephony.TelephonyPermissions;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080049
50import java.util.HashMap;
Grace Jia1ca70d02020-01-16 14:13:12 -080051import java.util.List;
52import java.util.Objects;
Rambo Wang979ccae2021-04-28 17:51:36 -070053import java.util.Set;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080054
55/**
56 * The class to provide base facility to access ServiceState related content,
57 * which is stored in a SQLite database.
58 */
59public 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
Grace Jiadd25f0e2020-01-21 12:35:28 -080065 /**
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 /**
Grace Jiadd25f0e2020-01-21 12:35:28 -080075 * 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
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800233 private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
Rambo Wang979ccae2021-04-28 17:51:36 -0700234
235 @VisibleForTesting
236 /* package */ static final String[] ALL_COLUMNS = {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800237 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,
Rambo Wangfbdb3482021-03-15 21:14:19 -0700261 DATA_NETWORK_TYPE,
Rambo Wang917ee2f2021-03-15 12:15:51 -0700262 DUPLEX_MODE,
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800263 };
264
Rambo Wang979ccae2021-04-28 17:51:36 -0700265 /**
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
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800293 @Override
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
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
315 public int getDefaultSubId() {
316 return SubscriptionManager.getDefaultSubscriptionId();
317 }
318
319 @Override
320 public Uri insert(Uri uri, ContentValues values) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800321 if (isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800322 // 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
359 public int delete(Uri uri, String selection, String[] selectionArgs) {
360 throw new RuntimeException("Not supported");
361 }
362
363 @Override
364 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
365 throw new RuntimeException("Not supported");
366 }
367
368 @Override
369 public String getType(Uri uri) {
370 throw new RuntimeException("Not supported");
371 }
372
373 @Override
374 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
375 String sortOrder) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800376 if (!isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800377 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
Rambo Wang979ccae2021-04-28 17:51:36 -0700395 ServiceState unredactedServiceState = getServiceState(subId);
396 if (unredactedServiceState == null) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800397 Log.d(TAG, "returning null");
398 return null;
399 }
400
Rambo Wang88c36272021-06-23 13:02:34 -0700401 // TODO(b/182384053): replace targetSdk check with CompatChanges#isChangeEnabled
Rambo Wang979ccae2021-04-28 17:51:36 -0700402 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
Rambo Wang88c36272021-06-23 13:02:34 -0700417 final boolean hasLocationPermission = hasLocationPermission();
Rambo Wang979ccae2021-04-28 17:51:36 -0700418 if (hasLocationPermission) {
Rambo Wang88c36272021-06-23 13:02:34 -0700419 // No matter the targetSdkVersion, return unredacted ServiceState if caller does
420 // have location permission.
Rambo Wang979ccae2021-04-28 17:51:36 -0700421 ss = unredactedServiceState;
422 } else {
Rambo Wang88c36272021-06-23 13:02:34 -0700423 // 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) {
Rambo Wang979ccae2021-04-28 17:51:36 -0700426 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
Rambo Wang88c36272021-06-23 13:02:34 -0700434 // In all other cases, return the redacted ServiceState.
Rambo Wang979ccae2021-04-28 17:51:36 -0700435 // 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.
Rambo Wang88c36272021-06-23 13:02:34 -0700439 ss = getLocationRedactedServiceState(unredactedServiceState);
Rambo Wang979ccae2021-04-28 17:51:36 -0700440 }
441 }
442
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800443 // Build the result
SongFerngWangf379b8b2019-12-23 18:24:44 +0800444 final int voice_reg_state = ss.getState();
445 final int data_reg_state = ss.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800446 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();
Rambo Wangfbdb3482021-03-15 21:14:19 -0700468 final int data_network_type = ss.getDataNetworkType();
Rambo Wang917ee2f2021-03-15 12:15:51 -0700469 final int duplex_mode = ss.getDuplexMode();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800470
Rambo Wang979ccae2021-04-28 17:51:36 -0700471 Object[] data = availableColumns == ALL_COLUMNS ? new Object[]{
472 // data for all columns
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800473 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,
Rambo Wangfbdb3482021-03-15 21:14:19 -0700497 data_network_type,
Rambo Wang917ee2f2021-03-15 12:15:51 -0700498 duplex_mode,
Rambo Wang979ccae2021-04-28 17:51:36 -0700499 } : 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);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800510 }
511 }
512
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
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 }
Rambo Wangfbdb3482021-03-15 21:14:19 -0700571 if (firstUpdate || dataNetworkTypeChanged(oldSS, newSS)) {
572 context.getContentResolver().notifyChange(
573 getUriForSubscriptionIdAndField(subId, DATA_NETWORK_TYPE), null, false);
574 }
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800575 }
576
577 private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800578 return oldSS.getState() != newSS.getState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800579 }
580
581 private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800582 return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800583 }
584
585 private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
586 return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
587 }
588
589 private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
590 return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
591 }
592
Rambo Wangfbdb3482021-03-15 21:14:19 -0700593 private static boolean dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS) {
594 return oldSS.getDataNetworkType() != newSS.getDataNetworkType();
595 }
596
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800597 /**
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
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)
Rambo Wangfbdb3482021-03-15 21:14:19 -0700616 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)
617 || dataNetworkTypeChanged(oldSS, newSS)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800618 context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
619 }
620 }
Grace Jia1ca70d02020-01-16 14:13:12 -0800621
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 */
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 }
Grace Jiadd25f0e2020-01-21 12:35:28 -0800646
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 */
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 }
Rambo Wang979ccae2021-04-28 17:51:36 -0700663
Rambo Wang88c36272021-06-23 13:02:34 -0700664 /**
665 * Check location permission with same policy as {@link TelephonyManager#getServiceState()}
666 * which enforces location permission check starting from Q.
667 */
668 private boolean hasLocationPermission() {
669 LocationAccessPolicy.LocationPermissionResult locationPermissionResult =
Rambo Wang979ccae2021-04-28 17:51:36 -0700670 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)
Rambo Wang88c36272021-06-23 13:02:34 -0700678 .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
679 .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
680 .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
Rambo Wang979ccae2021-04-28 17:51:36 -0700681 .build());
Rambo Wang88c36272021-06-23 13:02:34 -0700682 return locationPermissionResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
Rambo Wang979ccae2021-04-28 17:51:36 -0700683 }
684
Rambo Wang88c36272021-06-23 13:02:34 -0700685 // Return a copy of ServiceState with all sensitive info redacted.
686 @VisibleForTesting
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;
Rambo Wang979ccae2021-04-28 17:51:36 -0700693 }
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800694}