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