blob: a7d27d5e66e44e1114406fff07ad6a2219ed2899 [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
19import static android.provider.Telephony.ServiceStateTable;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080020import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080021import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080022import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080023import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
24import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
25
26import android.content.ContentProvider;
27import android.content.ContentValues;
28import android.content.Context;
29import android.database.Cursor;
30import android.database.MatrixCursor;
31import android.database.MatrixCursor.RowBuilder;
32import android.net.Uri;
33import android.os.Parcel;
34import android.telephony.ServiceState;
35import android.telephony.SubscriptionManager;
36import android.util.Log;
37
38import com.android.internal.annotations.VisibleForTesting;
39
40import java.util.HashMap;
Grace Jia1ca70d02020-01-16 14:13:12 -080041import java.util.List;
42import java.util.Objects;
SongFerngWang1bb5a6f2019-12-10 00:42:54 +080043
44/**
45 * The class to provide base facility to access ServiceState related content,
46 * which is stored in a SQLite database.
47 */
48public class ServiceStateProvider extends ContentProvider {
49 private static final String TAG = "ServiceStateProvider";
50
51 public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
52 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
53
Grace Jiadd25f0e2020-01-21 12:35:28 -080054 /**
55 * The current service state.
56 *
57 * This is the entire {@link ServiceState} object in byte array.
58 *
59 * @hide
60 */
61 public static final String SERVICE_STATE = "service_state";
62
63 /**
64 * An integer value indicating the current data service state.
65 * <p>
66 * Valid values: {@link ServiceState#STATE_IN_SERVICE},
67 * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
68 * {@link ServiceState#STATE_POWER_OFF}.
69 * <p>
70 * This is the same as {@link ServiceState#getDataRegState()}.
71 * @hide
72 */
73 public static final String DATA_REG_STATE = "data_reg_state";
74
75 /**
76 * An integer value indicating the current voice roaming type.
77 * <p>
78 * This is the same as {@link ServiceState#getVoiceRoamingType()}.
79 * @hide
80 */
81 public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
82
83 /**
84 * An integer value indicating the current data roaming type.
85 * <p>
86 * This is the same as {@link ServiceState#getDataRoamingType()}.
87 * @hide
88 */
89 public static final String DATA_ROAMING_TYPE = "data_roaming_type";
90
91 /**
92 * The current registered voice network operator name in long alphanumeric format.
93 * <p>
94 * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
95 * @hide
96 */
97 public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
98
99 /**
100 * The current registered operator name in short alphanumeric format.
101 * <p>
102 * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
103 * network operator name in long alphanumeric format.
104 * <p>
105 * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
106 * @hide
107 */
108 public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
109
110 /**
111 * The current registered operator numeric id.
112 * <p>
113 * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
114 * network code.
115 * <p>
116 * This is the same as {@link ServiceState#getOperatorNumeric()}.
117 */
118 public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
119
120 /**
121 * The current registered data network operator name in long alphanumeric format.
122 * <p>
123 * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
124 * @hide
125 */
126 public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
127
128 /**
129 * The current registered data network operator name in short alphanumeric format.
130 * <p>
131 * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
132 * @hide
133 */
134 public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
135
136 /**
137 * The current registered data network operator numeric id.
138 * <p>
139 * This is the same as {@link ServiceState#getOperatorNumeric()}.
140 * @hide
141 */
142 public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
143
144 /**
145 * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
146 * @hide
147 */
148 public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
149
150 /**
151 * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
152 * @hide
153 */
154 public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
155
156 /**
157 * This is the same as {@link ServiceState#getCssIndicator()}.
158 * @hide
159 */
160 public static final String CSS_INDICATOR = "css_indicator";
161
162 /**
163 * This is the same as {@link ServiceState#getCdmaNetworkId()}.
164 * @hide
165 */
166 public static final String NETWORK_ID = "network_id";
167
168 /**
169 * This is the same as {@link ServiceState#getCdmaSystemId()}.
170 * @hide
171 */
172 public static final String SYSTEM_ID = "system_id";
173
174 /**
175 * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
176 * @hide
177 */
178 public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
179
180 /**
181 * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
182 * @hide
183 */
184 public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
185 "cdma_default_roaming_indicator";
186
187 /**
188 * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
189 * @hide
190 */
191 public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
192
193 /**
194 * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
195 * @hide
196 */
197 public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
198
199 /**
200 * This is the same as {@link ServiceState#isEmergencyOnly()}.
201 * @hide
202 */
203 public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
204
205 /**
206 * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
207 * @hide
208 */
209 public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
210 "is_data_roaming_from_registration";
211
212 /**
213 * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
214 * @hide
215 */
216 public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
217
218 /**
219 * The current registered raw data network operator name in long alphanumeric format.
220 * <p>
221 * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}.
222 * @hide
223 */
224 public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw";
225
226 /**
227 * The current registered raw data network operator name in short alphanumeric format.
228 * <p>
229 * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}.
230 * @hide
231 */
232 public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
233
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800234 private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
235 private static final String[] sColumns = {
236 VOICE_REG_STATE,
237 DATA_REG_STATE,
238 VOICE_ROAMING_TYPE,
239 DATA_ROAMING_TYPE,
240 VOICE_OPERATOR_ALPHA_LONG,
241 VOICE_OPERATOR_ALPHA_SHORT,
242 VOICE_OPERATOR_NUMERIC,
243 DATA_OPERATOR_ALPHA_LONG,
244 DATA_OPERATOR_ALPHA_SHORT,
245 DATA_OPERATOR_NUMERIC,
246 IS_MANUAL_NETWORK_SELECTION,
247 RIL_VOICE_RADIO_TECHNOLOGY,
248 RIL_DATA_RADIO_TECHNOLOGY,
249 CSS_INDICATOR,
250 NETWORK_ID,
251 SYSTEM_ID,
252 CDMA_ROAMING_INDICATOR,
253 CDMA_DEFAULT_ROAMING_INDICATOR,
254 CDMA_ERI_ICON_INDEX,
255 CDMA_ERI_ICON_MODE,
256 IS_EMERGENCY_ONLY,
257 IS_USING_CARRIER_AGGREGATION,
258 OPERATOR_ALPHA_LONG_RAW,
259 OPERATOR_ALPHA_SHORT_RAW,
260 };
261
262 @Override
263 public boolean onCreate() {
264 return true;
265 }
266
267 /**
268 * Returns the {@link ServiceState} information on specified subscription.
269 *
270 * @param subId whose subscriber id is returned
271 * @return the {@link ServiceState} information on specified subscription.
272 */
273 @VisibleForTesting
274 public ServiceState getServiceState(int subId) {
275 return mServiceStates.get(subId);
276 }
277
278 /**
279 * Returns the system's default subscription id.
280 *
281 * @return the "system" default subscription id.
282 */
283 @VisibleForTesting
284 public int getDefaultSubId() {
285 return SubscriptionManager.getDefaultSubscriptionId();
286 }
287
288 @Override
289 public Uri insert(Uri uri, ContentValues values) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800290 if (isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800291 // Parse the subId
292 int subId = 0;
293 try {
294 subId = Integer.parseInt(uri.getLastPathSegment());
295 } catch (NumberFormatException e) {
296 Log.e(TAG, "insert: no subId provided in uri");
297 throw e;
298 }
299 Log.d(TAG, "subId=" + subId);
300
301 // handle DEFAULT_SUBSCRIPTION_ID
302 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
303 subId = getDefaultSubId();
304 }
305
306 final Parcel p = Parcel.obtain();
307 final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
308 p.unmarshall(rawBytes, 0, rawBytes.length);
309 p.setDataPosition(0);
310
311 // create the new service state
312 final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
313
314 // notify listeners
315 // if ss is null (e.g. first service state update) we will notify for all fields
316 ServiceState ss = getServiceState(subId);
317 notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
318 notifyChangeForSubId(getContext(), ss, newSS, subId);
319
320 // store the new service state
321 mServiceStates.put(subId, newSS);
322 return uri;
323 }
324 return null;
325 }
326
327 @Override
328 public int delete(Uri uri, String selection, String[] selectionArgs) {
329 throw new RuntimeException("Not supported");
330 }
331
332 @Override
333 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
334 throw new RuntimeException("Not supported");
335 }
336
337 @Override
338 public String getType(Uri uri) {
339 throw new RuntimeException("Not supported");
340 }
341
342 @Override
343 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
344 String sortOrder) {
Grace Jia1ca70d02020-01-16 14:13:12 -0800345 if (!isPathPrefixMatch(uri, CONTENT_URI)) {
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800346 throw new IllegalArgumentException("Invalid URI: " + uri);
347 } else {
348 // Parse the subId
349 int subId = 0;
350 try {
351 subId = Integer.parseInt(uri.getLastPathSegment());
352 } catch (NumberFormatException e) {
353 Log.d(TAG, "query: no subId provided in uri, using default.");
354 subId = getDefaultSubId();
355 }
356 Log.d(TAG, "subId=" + subId);
357
358 // handle DEFAULT_SUBSCRIPTION_ID
359 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
360 subId = getDefaultSubId();
361 }
362
363 // Get the service state
364 ServiceState ss = getServiceState(subId);
365 if (ss == null) {
366 Log.d(TAG, "returning null");
367 return null;
368 }
369
370 // Build the result
SongFerngWangf379b8b2019-12-23 18:24:44 +0800371 final int voice_reg_state = ss.getState();
372 final int data_reg_state = ss.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800373 final int voice_roaming_type = ss.getVoiceRoamingType();
374 final int data_roaming_type = ss.getDataRoamingType();
375 final String voice_operator_alpha_long = ss.getOperatorAlphaLong();
376 final String voice_operator_alpha_short = ss.getOperatorAlphaShort();
377 final String voice_operator_numeric = ss.getOperatorNumeric();
378 final String data_operator_alpha_long = ss.getOperatorAlphaLong();
379 final String data_operator_alpha_short = ss.getOperatorAlphaShort();
380 final String data_operator_numeric = ss.getOperatorNumeric();
381 final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
382 final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
383 final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
384 final int css_indicator = ss.getCssIndicator();
385 final int network_id = ss.getCdmaNetworkId();
386 final int system_id = ss.getCdmaSystemId();
387 final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
388 final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
389 final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
390 final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
391 final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
392 final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
393 final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
394 final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
395
396 return buildSingleRowResult(projection, sColumns, new Object[] {
397 voice_reg_state,
398 data_reg_state,
399 voice_roaming_type,
400 data_roaming_type,
401 voice_operator_alpha_long,
402 voice_operator_alpha_short,
403 voice_operator_numeric,
404 data_operator_alpha_long,
405 data_operator_alpha_short,
406 data_operator_numeric,
407 is_manual_network_selection,
408 ril_voice_radio_technology,
409 ril_data_radio_technology,
410 css_indicator,
411 network_id,
412 system_id,
413 cdma_roaming_indicator,
414 cdma_default_roaming_indicator,
415 cdma_eri_icon_index,
416 cdma_eri_icon_mode,
417 is_emergency_only,
418 is_using_carrier_aggregation,
419 operator_alpha_long_raw,
420 operator_alpha_short_raw,
421 });
422 }
423 }
424
425 private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
426 Object[] data) {
427 if (projection == null) {
428 projection = availableColumns;
429 }
430 final MatrixCursor c = new MatrixCursor(projection, 1);
431 final RowBuilder row = c.newRow();
432 for (int i = 0; i < c.getColumnCount(); i++) {
433 final String columnName = c.getColumnName(i);
434 boolean found = false;
435 for (int j = 0; j < availableColumns.length; j++) {
436 if (availableColumns[j].equals(columnName)) {
437 row.add(data[j]);
438 found = true;
439 break;
440 }
441 }
442 if (!found) {
443 throw new IllegalArgumentException("Invalid column " + projection[i]);
444 }
445 }
446 return c;
447 }
448
449 /**
450 * Notify interested apps that certain fields of the ServiceState have changed.
451 *
452 * Apps which want to wake when specific fields change can use
453 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
454 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
455 *
456 * We will only notify for certain fields. This is an intentional change from the behavior of
457 * the broadcast. Listeners will be notified when the voice or data registration state or
458 * roaming type changes.
459 */
460 @VisibleForTesting
461 public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
462 ServiceState newSS, int subId) {
463 final boolean firstUpdate = (oldSS == null) ? true : false;
464
465 // for every field, if the field has changed values, notify via the provider
466 if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
467 context.getContentResolver().notifyChange(
468 getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
469 /* observer= */ null, /* syncToNetwork= */ false);
470 }
471 if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
472 context.getContentResolver().notifyChange(
473 getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
474 }
475 if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
476 context.getContentResolver().notifyChange(
477 getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
478 }
479 if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
480 context.getContentResolver().notifyChange(
481 getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
482 }
483 }
484
485 private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800486 return oldSS.getState() != newSS.getState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800487 }
488
489 private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
SongFerngWangf379b8b2019-12-23 18:24:44 +0800490 return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800491 }
492
493 private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
494 return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
495 }
496
497 private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
498 return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
499 }
500
501 /**
502 * Notify interested apps that the ServiceState has changed.
503 *
504 * Apps which want to wake when any field in the ServiceState has changed can use
505 * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
506 * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
507 *
508 * We will only notify for certain fields. This is an intentional change from the behavior of
509 * the broadcast. Listeners will only be notified when the voice/data registration state or
510 * roaming type changes.
511 */
512 @VisibleForTesting
513 public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
514 int subId) {
515 // if the voice or data registration or roaming state field has changed values, notify via
516 // the provider.
517 // If oldSS is null and newSS is not (e.g. first update of service state) this will also
518 // notify
519 if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
520 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
521 context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
522 }
523 }
Grace Jia1ca70d02020-01-16 14:13:12 -0800524
525 /**
526 * Test if this is a path prefix match against the given Uri. Verifies that
527 * scheme, authority, and atomic path segments match.
528 *
529 * Copied from frameworks/base/core/java/android/net/Uri.java
530 */
531 private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
532 if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
533 if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
534
535 List<String> segA = uriA.getPathSegments();
536 List<String> segB = uriB.getPathSegments();
537
538 final int size = segB.size();
539 if (segA.size() < size) return false;
540
541 for (int i = 0; i < size; i++) {
542 if (!Objects.equals(segA.get(i), segB.get(i))) {
543 return false;
544 }
545 }
546
547 return true;
548 }
Grace Jiadd25f0e2020-01-21 12:35:28 -0800549
550 /**
551 * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
552 *
553 * @param state the ServiceState to convert into ContentValues
554 * @return the convertedContentValues instance
555 * @hide
556 */
557 public static ContentValues getContentValuesForServiceState(ServiceState state) {
558 ContentValues values = new ContentValues();
559 final Parcel p = Parcel.obtain();
560 state.writeToParcel(p, 0);
561 // Turn the parcel to byte array. Safe to do this because the content values were never
562 // written into a persistent storage. ServiceStateProvider keeps values in the memory.
563 values.put(SERVICE_STATE, p.marshall());
564 return values;
565 }
SongFerngWang1bb5a6f2019-12-10 00:42:54 +0800566}