blob: 894d1c7e02c2a67c84badd53867e1375791e343a [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;
Rambo Wang1bf0afc2021-06-25 11:53:44 -070031import android.app.compat.CompatChanges;
32import android.compat.annotation.ChangeId;
33import android.compat.annotation.EnabledAfter;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080034import android.content.ContentProvider;
35import android.content.ContentValues;
36import android.content.Context;
37import android.database.Cursor;
38import android.database.MatrixCursor;
39import android.database.MatrixCursor.RowBuilder;
40import android.net.Uri;
Rambo Wang979ccae2021-04-28 17:51:36 -070041import android.os.Binder;
42import android.os.Build;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080043import android.os.Parcel;
Sangyun Yunf7343182024-03-19 02:30:36 +000044import android.os.UserHandle;
Rambo Wang979ccae2021-04-28 17:51:36 -070045import android.telephony.LocationAccessPolicy;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080046import android.telephony.ServiceState;
47import android.telephony.SubscriptionManager;
Rambo Wang88c36272021-06-23 13:02:34 -070048import android.telephony.TelephonyManager;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080049import android.util.Log;
50
51import com.android.internal.annotations.VisibleForTesting;
Rambo Wang979ccae2021-04-28 17:51:36 -070052import com.android.internal.telephony.TelephonyPermissions;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080053
54import java.util.HashMap;
Grace Jia1ca70d02020-01-16 14:13:12 -080055import java.util.List;
56import java.util.Objects;
Rambo Wang979ccae2021-04-28 17:51:36 -070057import java.util.Set;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080058
59/**
60 * The class to provide base facility to access ServiceState related content,
61 * which is stored in a SQLite database.
62 */
63public 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
Grace Jiadd25f0e2020-01-21 12:35:28 -080069 /**
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 /**
Grace Jiadd25f0e2020-01-21 12:35:28 -080079 * 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
Rambo Wang1bf0afc2021-06-25 11:53:44 -0700237 /**
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
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800246 private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
Rambo Wang979ccae2021-04-28 17:51:36 -0700247
248 @VisibleForTesting
249 /* package */ static final String[] ALL_COLUMNS = {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800250 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,
Rambo Wangfbdb3482021-03-15 21:14:19 -0700274 DATA_NETWORK_TYPE,
Rambo Wang917ee2f2021-03-15 12:15:51 -0700275 DUPLEX_MODE,
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800276 };
277
Rambo Wang979ccae2021-04-28 17:51:36 -0700278 /**
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
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800306 @Override
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
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
328 public int getDefaultSubId() {
329 return SubscriptionManager.getDefaultSubscriptionId();
330 }
331
332 @Override
333 public Uri insert(Uri uri, ContentValues values) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800334 if (isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800335 // 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
372 public int delete(Uri uri, String selection, String[] selectionArgs) {
373 throw new RuntimeException("Not supported");
374 }
375
376 @Override
377 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
378 throw new RuntimeException("Not supported");
379 }
380
381 @Override
382 public String getType(Uri uri) {
383 throw new RuntimeException("Not supported");
384 }
385
386 @Override
387 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
388 String sortOrder) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800389 if (!isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800390 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
Rambo Wang979ccae2021-04-28 17:51:36 -0700408 ServiceState unredactedServiceState = getServiceState(subId);
409 if (unredactedServiceState == null) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800410 Log.d(TAG, "returning null");
411 return null;
412 }
413
Rambo Wang1bf0afc2021-06-25 11:53:44 -0700414 final boolean enforceLocationPermission =
415 CompatChanges.isChangeEnabled(ENFORCE_LOCATION_PERMISSION_CHECK);
Rambo Wang979ccae2021-04-28 17:51:36 -0700416 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;
Rambo Wang1bf0afc2021-06-25 11:53:44 -0700423 if (enforceLocationPermission && targetingAtLeastS && !canReadPrivilegedPhoneState) {
Rambo Wang979ccae2021-04-28 17:51:36 -0700424 // 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;
Rambo Wang353652c2022-03-02 12:22:21 -0800430 if (!enforceLocationPermission) {
431 // No matter the targetSdkVersion, return unredacted ServiceState if location
432 // permission enforcement is not introduced
Rambo Wang979ccae2021-04-28 17:51:36 -0700433 ss = unredactedServiceState;
434 } else {
Rambo Wang353652c2022-03-02 12:22:21 -0800435 boolean implicitlyQueryLocation = projection == null;
436 boolean explicitlyQueryLocation = false;
437 if (projection != null) {
Rambo Wang979ccae2021-04-28 17:51:36 -0700438 for (String requiredColumn : projection) {
439 if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) {
Rambo Wang353652c2022-03-02 12:22:21 -0800440 explicitlyQueryLocation = true;
441 break;
Rambo Wang979ccae2021-04-28 17:51:36 -0700442 }
443 }
444 }
445
Rambo Wang353652c2022-03-02 12:22:21 -0800446 // 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 }
Rambo Wang979ccae2021-04-28 17:51:36 -0700468 }
469 }
470
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800471 // Build the result
SongFerngWangf379b8b2019-12-23 18:24:44 +0800472 final int voice_reg_state = ss.getState();
473 final int data_reg_state = ss.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800474 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();
Rambo Wangfbdb3482021-03-15 21:14:19 -0700496 final int data_network_type = ss.getDataNetworkType();
Rambo Wang917ee2f2021-03-15 12:15:51 -0700497 final int duplex_mode = ss.getDuplexMode();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800498
Rambo Wang979ccae2021-04-28 17:51:36 -0700499 Object[] data = availableColumns == ALL_COLUMNS ? new Object[]{
500 // data for all columns
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800501 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,
Rambo Wangfbdb3482021-03-15 21:14:19 -0700525 data_network_type,
Rambo Wang917ee2f2021-03-15 12:15:51 -0700526 duplex_mode,
Rambo Wang979ccae2021-04-28 17:51:36 -0700527 } : 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);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800538 }
539 }
540
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
577 public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
578 ServiceState newSS, int subId) {
579 final boolean firstUpdate = (oldSS == null) ? true : false;
580
Sangyun Yunf7343182024-03-19 02:30:36 +0000581 // For every field, if the field has changed values, notify via the provider to all users
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800582 if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
583 context.getContentResolver().notifyChange(
584 getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
Sangyun Yunf7343182024-03-19 02:30:36 +0000585 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800586 }
587 if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
588 context.getContentResolver().notifyChange(
Sangyun Yunf7343182024-03-19 02:30:36 +0000589 getUriForSubscriptionIdAndField(subId, DATA_REG_STATE),
590 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800591 }
592 if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
593 context.getContentResolver().notifyChange(
Sangyun Yunf7343182024-03-19 02:30:36 +0000594 getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE),
595 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800596 }
597 if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
598 context.getContentResolver().notifyChange(
Sangyun Yunf7343182024-03-19 02:30:36 +0000599 getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE),
600 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800601 }
Rambo Wangfbdb3482021-03-15 21:14:19 -0700602 if (firstUpdate || dataNetworkTypeChanged(oldSS, newSS)) {
603 context.getContentResolver().notifyChange(
Sangyun Yunf7343182024-03-19 02:30:36 +0000604 getUriForSubscriptionIdAndField(subId, DATA_NETWORK_TYPE),
605 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
Rambo Wangfbdb3482021-03-15 21:14:19 -0700606 }
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800607 }
608
609 private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800610 return oldSS.getState() != newSS.getState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800611 }
612
613 private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800614 return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800615 }
616
617 private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
618 return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
619 }
620
621 private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
622 return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
623 }
624
Rambo Wangfbdb3482021-03-15 21:14:19 -0700625 private static boolean dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS) {
626 return oldSS.getDataNetworkType() != newSS.getDataNetworkType();
627 }
628
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800629 /**
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
641 public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
642 int subId) {
Sangyun Yunf7343182024-03-19 02:30:36 +0000643 // If the voice or data registration or roaming state field has changed values, notify via
644 // the provider to all users.
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800645 // If oldSS is null and newSS is not (e.g. first update of service state) this will also
Sangyun Yunf7343182024-03-19 02:30:36 +0000646 // notify to all users.
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800647 if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
Rambo Wangfbdb3482021-03-15 21:14:19 -0700648 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)
649 || dataNetworkTypeChanged(oldSS, newSS)) {
Sangyun Yunf7343182024-03-19 02:30:36 +0000650 context.getContentResolver().notifyChange(getUriForSubscriptionId(subId),
651 /* observer= */ null, /* syncToNetwork= */ false, UserHandle.USER_ALL);
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800652 }
653 }
Grace Jia1ca70d02020-01-16 14:13:12 -0800654
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 */
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 }
Grace Jiadd25f0e2020-01-21 12:35:28 -0800679
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 */
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 }
Rambo Wang979ccae2021-04-28 17:51:36 -0700696
Rambo Wang88c36272021-06-23 13:02:34 -0700697 /**
698 * Check location permission with same policy as {@link TelephonyManager#getServiceState()}
699 * which enforces location permission check starting from Q.
700 */
701 private boolean hasLocationPermission() {
702 LocationAccessPolicy.LocationPermissionResult locationPermissionResult =
Rambo Wang979ccae2021-04-28 17:51:36 -0700703 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)
Rambo Wang88c36272021-06-23 13:02:34 -0700711 .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
712 .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
713 .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
Rambo Wang979ccae2021-04-28 17:51:36 -0700714 .build());
Rambo Wang88c36272021-06-23 13:02:34 -0700715 return locationPermissionResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
Rambo Wang979ccae2021-04-28 17:51:36 -0700716 }
717
Rambo Wang88c36272021-06-23 13:02:34 -0700718 // Return a copy of ServiceState with all sensitive info redacted.
719 @VisibleForTesting
720 /* package */ static ServiceState getLocationRedactedServiceState(ServiceState serviceState) {
721 ServiceState ss =
722 serviceState.createLocationInfoSanitizedCopy(true /*removeCoarseLocation*/);
Rambo Wang88c36272021-06-23 13:02:34 -0700723 return ss;
Rambo Wang979ccae2021-04-28 17:51:36 -0700724 }
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800725}