1 /*
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 
17 package com.android.phone;
18 
19 import static android.provider.Telephony.ServiceStateTable;
20 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
21 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
22 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
23 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
24 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
25 
26 import android.content.ContentProvider;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.database.Cursor;
30 import android.database.MatrixCursor;
31 import android.database.MatrixCursor.RowBuilder;
32 import android.net.Uri;
33 import android.os.Parcel;
34 import android.telephony.ServiceState;
35 import android.telephony.SubscriptionManager;
36 import android.util.Log;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Objects;
43 
44 /**
45  * The class to provide base facility to access ServiceState related content,
46  * which is stored in a SQLite database.
47  */
48 public 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 
54     /**
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 
234     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
onCreate()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
getServiceState(int subId)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
getDefaultSubId()284     public int getDefaultSubId() {
285         return SubscriptionManager.getDefaultSubscriptionId();
286     }
287 
288     @Override
insert(Uri uri, ContentValues values)289     public Uri insert(Uri uri, ContentValues values) {
290         if (isPathPrefixMatch(uri, CONTENT_URI)) {
291             // 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
delete(Uri uri, String selection, String[] selectionArgs)328     public int delete(Uri uri, String selection, String[] selectionArgs) {
329         throw new RuntimeException("Not supported");
330     }
331 
332     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)333     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
334         throw new RuntimeException("Not supported");
335     }
336 
337     @Override
getType(Uri uri)338     public String getType(Uri uri) {
339         throw new RuntimeException("Not supported");
340     }
341 
342     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)343     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
344             String sortOrder) {
345         if (!isPathPrefixMatch(uri, CONTENT_URI)) {
346             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
371             final int voice_reg_state = ss.getState();
372             final int data_reg_state = ss.getDataRegistrationState();
373             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 
buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)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
notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)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 
voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)485     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
486         return oldSS.getState() != newSS.getState();
487     }
488 
dataRegStateChanged(ServiceState oldSS, ServiceState newSS)489     private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
490         return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
491     }
492 
voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)493     private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
494         return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
495     }
496 
dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)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
notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)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     }
524 
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      */
isPathPrefixMatch(Uri uriA, Uri uriB)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     }
549 
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      */
getContentValuesForServiceState(ServiceState state)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     }
566 }
567