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