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.content.pm.PackageManager.PERMISSION_GRANTED;
20 import static android.provider.Telephony.ServiceStateTable;
21 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
22 import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
23 import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
24 import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE;
25 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
26 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
27 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
28 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
29 
30 import android.Manifest;
31 import android.content.ContentProvider;
32 import android.content.ContentValues;
33 import android.content.Context;
34 import android.database.Cursor;
35 import android.database.MatrixCursor;
36 import android.database.MatrixCursor.RowBuilder;
37 import android.net.Uri;
38 import android.os.Binder;
39 import android.os.Build;
40 import android.os.Parcel;
41 import android.telephony.LocationAccessPolicy;
42 import android.telephony.ServiceState;
43 import android.telephony.SubscriptionManager;
44 import android.telephony.TelephonyManager;
45 import android.util.Log;
46 
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.telephony.TelephonyPermissions;
49 
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.Set;
54 
55 /**
56  * The class to provide base facility to access ServiceState related content,
57  * which is stored in a SQLite database.
58  */
59 public class ServiceStateProvider extends ContentProvider {
60     private static final String TAG = "ServiceStateProvider";
61 
62     public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
63     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
64 
65     /**
66      * The current service state.
67      *
68      * This is the entire {@link ServiceState} object in byte array.
69      *
70      * @hide
71      */
72     public static final String SERVICE_STATE = "service_state";
73 
74     /**
75      * An integer value indicating the current voice roaming type.
76      * <p>
77      * This is the same as {@link ServiceState#getVoiceRoamingType()}.
78      * @hide
79      */
80     public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
81 
82     /**
83      * An integer value indicating the current data roaming type.
84      * <p>
85      * This is the same as {@link ServiceState#getDataRoamingType()}.
86      * @hide
87      */
88     public static final String DATA_ROAMING_TYPE = "data_roaming_type";
89 
90     /**
91      * The current registered voice network operator name in long alphanumeric format.
92      * <p>
93      * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
94      * @hide
95      */
96     public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
97 
98     /**
99      * The current registered operator name in short alphanumeric format.
100      * <p>
101      * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
102      * network operator name in long alphanumeric format.
103      * <p>
104      * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
105      * @hide
106      */
107     public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
108 
109     /**
110      * The current registered operator numeric id.
111      * <p>
112      * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
113      * network code.
114      * <p>
115      * This is the same as {@link ServiceState#getOperatorNumeric()}.
116      */
117     public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
118 
119     /**
120      * The current registered data network operator name in long alphanumeric format.
121      * <p>
122      * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
123      * @hide
124      */
125     public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
126 
127     /**
128      * The current registered data network operator name in short alphanumeric format.
129      * <p>
130      * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
131      * @hide
132      */
133     public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
134 
135     /**
136      * The current registered data network operator numeric id.
137      * <p>
138      * This is the same as {@link ServiceState#getOperatorNumeric()}.
139      * @hide
140      */
141     public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
142 
143     /**
144      * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
145      * @hide
146      */
147     public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
148 
149     /**
150      * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
151      * @hide
152      */
153     public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
154 
155     /**
156      * This is the same as {@link ServiceState#getCssIndicator()}.
157      * @hide
158      */
159     public static final String CSS_INDICATOR = "css_indicator";
160 
161     /**
162      * This is the same as {@link ServiceState#getCdmaNetworkId()}.
163      * @hide
164      */
165     public static final String NETWORK_ID = "network_id";
166 
167     /**
168      * This is the same as {@link ServiceState#getCdmaSystemId()}.
169      * @hide
170      */
171     public static final String SYSTEM_ID = "system_id";
172 
173     /**
174      * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
175      * @hide
176      */
177     public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
178 
179     /**
180      * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
181      * @hide
182      */
183     public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
184             "cdma_default_roaming_indicator";
185 
186     /**
187      * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
188      * @hide
189      */
190     public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
191 
192     /**
193      * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
194      * @hide
195      */
196     public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
197 
198     /**
199      * This is the same as {@link ServiceState#isEmergencyOnly()}.
200      * @hide
201      */
202     public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
203 
204     /**
205      * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
206      * @hide
207      */
208     public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
209             "is_data_roaming_from_registration";
210 
211     /**
212      * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
213      * @hide
214      */
215     public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
216 
217     /**
218      * The current registered raw data network operator name in long alphanumeric format.
219      * <p>
220      * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}.
221      * @hide
222      */
223     public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw";
224 
225     /**
226      * The current registered raw data network operator name in short alphanumeric format.
227      * <p>
228      * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}.
229      * @hide
230      */
231     public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
232 
233     private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
234 
235     @VisibleForTesting
236     /* package */ static final String[] ALL_COLUMNS = {
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,
261         DATA_NETWORK_TYPE,
262         DUPLEX_MODE,
263     };
264 
265     /**
266      * Columns that are exposed to public surface.
267      * These are the columns accessible to apps target S+ and lack
268      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} permission.
269      */
270     @VisibleForTesting
271     /* package */ static final String[] PUBLIC_COLUMNS = {
272             VOICE_REG_STATE,
273             DATA_REG_STATE,
274             VOICE_OPERATOR_NUMERIC,
275             IS_MANUAL_NETWORK_SELECTION,
276             DATA_NETWORK_TYPE,
277             DUPLEX_MODE
278     };
279 
280     /**
281      * Columns protected by location permissions (either FINE or COARSE).
282      * SecurityException will throw if applications without location permissions try to put those
283      * columns explicitly into cursor (e.g. through {@code projection} parameter in
284      * {@link #query(Uri, String[], String, String[], String)} method).
285      * Default (scrub-out) value will return if applications try to put all columns into cursor by
286      * specifying null of {@code projection} parameter and get values through the returned cursor.
287      */
288     private static final Set<String> LOCATION_PROTECTED_COLUMNS_SET = Set.of(
289             NETWORK_ID,
290             SYSTEM_ID
291     );
292 
293     @Override
onCreate()294     public boolean onCreate() {
295         return true;
296     }
297 
298     /**
299      * Returns the {@link ServiceState} information on specified subscription.
300      *
301      * @param subId whose subscriber id is returned
302      * @return the {@link ServiceState} information on specified subscription.
303      */
304     @VisibleForTesting
getServiceState(int subId)305     public ServiceState getServiceState(int subId) {
306         return mServiceStates.get(subId);
307     }
308 
309     /**
310      * Returns the system's default subscription id.
311      *
312      * @return the "system" default subscription id.
313      */
314     @VisibleForTesting
getDefaultSubId()315     public int getDefaultSubId() {
316         return SubscriptionManager.getDefaultSubscriptionId();
317     }
318 
319     @Override
insert(Uri uri, ContentValues values)320     public Uri insert(Uri uri, ContentValues values) {
321         if (isPathPrefixMatch(uri, CONTENT_URI)) {
322             // Parse the subId
323             int subId = 0;
324             try {
325                 subId = Integer.parseInt(uri.getLastPathSegment());
326             } catch (NumberFormatException e) {
327                 Log.e(TAG, "insert: no subId provided in uri");
328                 throw e;
329             }
330             Log.d(TAG, "subId=" + subId);
331 
332             // handle DEFAULT_SUBSCRIPTION_ID
333             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
334                 subId = getDefaultSubId();
335             }
336 
337             final Parcel p = Parcel.obtain();
338             final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
339             p.unmarshall(rawBytes, 0, rawBytes.length);
340             p.setDataPosition(0);
341 
342             // create the new service state
343             final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
344 
345             // notify listeners
346             // if ss is null (e.g. first service state update) we will notify for all fields
347             ServiceState ss = getServiceState(subId);
348             notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
349             notifyChangeForSubId(getContext(), ss, newSS, subId);
350 
351             // store the new service state
352             mServiceStates.put(subId, newSS);
353             return uri;
354         }
355         return null;
356     }
357 
358     @Override
delete(Uri uri, String selection, String[] selectionArgs)359     public int delete(Uri uri, String selection, String[] selectionArgs) {
360         throw new RuntimeException("Not supported");
361     }
362 
363     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)364     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
365         throw new RuntimeException("Not supported");
366     }
367 
368     @Override
getType(Uri uri)369     public String getType(Uri uri) {
370         throw new RuntimeException("Not supported");
371     }
372 
373     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)374     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
375             String sortOrder) {
376         if (!isPathPrefixMatch(uri, CONTENT_URI)) {
377             throw new IllegalArgumentException("Invalid URI: " + uri);
378         } else {
379             // Parse the subId
380             int subId = 0;
381             try {
382                 subId = Integer.parseInt(uri.getLastPathSegment());
383             } catch (NumberFormatException e) {
384                 Log.d(TAG, "query: no subId provided in uri, using default.");
385                 subId = getDefaultSubId();
386             }
387             Log.d(TAG, "subId=" + subId);
388 
389             // handle DEFAULT_SUBSCRIPTION_ID
390             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
391                 subId = getDefaultSubId();
392             }
393 
394             // Get the service state
395             ServiceState unredactedServiceState = getServiceState(subId);
396             if (unredactedServiceState == null) {
397                 Log.d(TAG, "returning null");
398                 return null;
399             }
400 
401             // TODO(b/182384053): replace targetSdk check with CompatChanges#isChangeEnabled
402             final boolean targetingAtLeastS = TelephonyPermissions.getTargetSdk(getContext(),
403                     getCallingPackage()) >= Build.VERSION_CODES.S;
404             final boolean canReadPrivilegedPhoneState = getContext().checkCallingOrSelfPermission(
405                     Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PERMISSION_GRANTED;
406 
407             final String[] availableColumns;
408             final ServiceState ss;
409             if (targetingAtLeastS && !canReadPrivilegedPhoneState) {
410                 // targetSdkVersion S+ without read privileged phone state permission can only
411                 // access public columns which have no location sensitive info.
412                 availableColumns = PUBLIC_COLUMNS;
413                 ss = unredactedServiceState;
414             } else {
415                 availableColumns = ALL_COLUMNS;
416 
417                 final boolean hasLocationPermission = hasLocationPermission();
418                 if (hasLocationPermission) {
419                     // No matter the targetSdkVersion, return unredacted ServiceState if caller does
420                     // have location permission.
421                     ss = unredactedServiceState;
422                 } else {
423                     // The caller has targetSdkVersion S+ but no location permission. It explicitly
424                     // requires location protected columns. Throw SecurityException to fail loudly.
425                     if (targetingAtLeastS && projection != null) {
426                         for (String requiredColumn : projection) {
427                             if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) {
428                                 throw new SecurityException("Column " + requiredColumn
429                                         + "requires location permissions to access.");
430                             }
431                         }
432                     }
433 
434                     // In all other cases, return the redacted ServiceState.
435                     // The caller has no location permission but only requires columns without
436                     // location sensitive info or "all" columns, return result that scrub out all
437                     // sensitive info. In later case, we will not know which columns will be fetched
438                     // from the returned cursor until the result has been returned.
439                     ss = getLocationRedactedServiceState(unredactedServiceState);
440                 }
441             }
442 
443             // Build the result
444             final int voice_reg_state = ss.getState();
445             final int data_reg_state = ss.getDataRegistrationState();
446             final int voice_roaming_type = ss.getVoiceRoamingType();
447             final int data_roaming_type = ss.getDataRoamingType();
448             final String voice_operator_alpha_long = ss.getOperatorAlphaLong();
449             final String voice_operator_alpha_short = ss.getOperatorAlphaShort();
450             final String voice_operator_numeric = ss.getOperatorNumeric();
451             final String data_operator_alpha_long = ss.getOperatorAlphaLong();
452             final String data_operator_alpha_short = ss.getOperatorAlphaShort();
453             final String data_operator_numeric = ss.getOperatorNumeric();
454             final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
455             final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
456             final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
457             final int css_indicator = ss.getCssIndicator();
458             final int network_id = ss.getCdmaNetworkId();
459             final int system_id = ss.getCdmaSystemId();
460             final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
461             final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
462             final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
463             final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
464             final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
465             final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
466             final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
467             final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
468             final int data_network_type = ss.getDataNetworkType();
469             final int duplex_mode = ss.getDuplexMode();
470 
471             Object[] data = availableColumns == ALL_COLUMNS ? new Object[]{
472                     // data for all columns
473                     voice_reg_state,
474                     data_reg_state,
475                     voice_roaming_type,
476                     data_roaming_type,
477                     voice_operator_alpha_long,
478                     voice_operator_alpha_short,
479                     voice_operator_numeric,
480                     data_operator_alpha_long,
481                     data_operator_alpha_short,
482                     data_operator_numeric,
483                     is_manual_network_selection,
484                     ril_voice_radio_technology,
485                     ril_data_radio_technology,
486                     css_indicator,
487                     network_id,
488                     system_id,
489                     cdma_roaming_indicator,
490                     cdma_default_roaming_indicator,
491                     cdma_eri_icon_index,
492                     cdma_eri_icon_mode,
493                     is_emergency_only,
494                     is_using_carrier_aggregation,
495                     operator_alpha_long_raw,
496                     operator_alpha_short_raw,
497                     data_network_type,
498                     duplex_mode,
499             } : new Object[]{
500                     // data for public columns only
501                     voice_reg_state,
502                     data_reg_state,
503                     voice_operator_numeric,
504                     is_manual_network_selection,
505                     data_network_type,
506                     duplex_mode,
507             };
508 
509             return buildSingleRowResult(projection, availableColumns, data);
510         }
511     }
512 
buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)513     private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
514             Object[] data) {
515         if (projection == null) {
516             projection = availableColumns;
517         }
518         final MatrixCursor c = new MatrixCursor(projection, 1);
519         final RowBuilder row = c.newRow();
520         for (int i = 0; i < c.getColumnCount(); i++) {
521             final String columnName = c.getColumnName(i);
522             boolean found = false;
523             for (int j = 0; j < availableColumns.length; j++) {
524                 if (availableColumns[j].equals(columnName)) {
525                     row.add(data[j]);
526                     found = true;
527                     break;
528                 }
529             }
530             if (!found) {
531                 throw new IllegalArgumentException("Invalid column " + projection[i]);
532             }
533         }
534         return c;
535     }
536 
537     /**
538      * Notify interested apps that certain fields of the ServiceState have changed.
539      *
540      * Apps which want to wake when specific fields change can use
541      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
542      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
543      *
544      * We will only notify for certain fields. This is an intentional change from the behavior of
545      * the broadcast. Listeners will be notified when the voice or data registration state or
546      * roaming type changes.
547      */
548     @VisibleForTesting
notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)549     public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
550             ServiceState newSS, int subId) {
551         final boolean firstUpdate = (oldSS == null) ? true : false;
552 
553         // for every field, if the field has changed values, notify via the provider
554         if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
555             context.getContentResolver().notifyChange(
556                     getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
557                     /* observer= */ null, /* syncToNetwork= */ false);
558         }
559         if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
560             context.getContentResolver().notifyChange(
561                     getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
562         }
563         if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
564             context.getContentResolver().notifyChange(
565                     getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
566         }
567         if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
568             context.getContentResolver().notifyChange(
569                     getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
570         }
571         if (firstUpdate || dataNetworkTypeChanged(oldSS, newSS)) {
572             context.getContentResolver().notifyChange(
573                     getUriForSubscriptionIdAndField(subId, DATA_NETWORK_TYPE), null, false);
574         }
575     }
576 
voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)577     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
578         return oldSS.getState() != newSS.getState();
579     }
580 
dataRegStateChanged(ServiceState oldSS, ServiceState newSS)581     private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
582         return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
583     }
584 
voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)585     private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
586         return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
587     }
588 
dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)589     private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
590         return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
591     }
592 
dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS)593     private static boolean dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS) {
594         return oldSS.getDataNetworkType() != newSS.getDataNetworkType();
595     }
596 
597     /**
598      * Notify interested apps that the ServiceState has changed.
599      *
600      * Apps which want to wake when any field in the ServiceState has changed can use
601      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
602      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
603      *
604      * We will only notify for certain fields. This is an intentional change from the behavior of
605      * the broadcast. Listeners will only be notified when the voice/data registration state or
606      * roaming type changes.
607      */
608     @VisibleForTesting
notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)609     public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
610             int subId) {
611         // if the voice or data registration or roaming state field has changed values, notify via
612         // the provider.
613         // If oldSS is null and newSS is not (e.g. first update of service state) this will also
614         // notify
615         if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
616                 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)
617                 || dataNetworkTypeChanged(oldSS, newSS)) {
618             context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
619         }
620     }
621 
622     /**
623      * Test if this is a path prefix match against the given Uri. Verifies that
624      * scheme, authority, and atomic path segments match.
625      *
626      * Copied from frameworks/base/core/java/android/net/Uri.java
627      */
isPathPrefixMatch(Uri uriA, Uri uriB)628     private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
629         if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
630         if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
631 
632         List<String> segA = uriA.getPathSegments();
633         List<String> segB = uriB.getPathSegments();
634 
635         final int size = segB.size();
636         if (segA.size() < size) return false;
637 
638         for (int i = 0; i < size; i++) {
639             if (!Objects.equals(segA.get(i), segB.get(i))) {
640                 return false;
641             }
642         }
643 
644         return true;
645     }
646 
647     /**
648      * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
649      *
650      * @param state the ServiceState to convert into ContentValues
651      * @return the convertedContentValues instance
652      * @hide
653      */
getContentValuesForServiceState(ServiceState state)654     public static ContentValues getContentValuesForServiceState(ServiceState state) {
655         ContentValues values = new ContentValues();
656         final Parcel p = Parcel.obtain();
657         state.writeToParcel(p, 0);
658         // Turn the parcel to byte array. Safe to do this because the content values were never
659         // written into a persistent storage. ServiceStateProvider keeps values in the memory.
660         values.put(SERVICE_STATE, p.marshall());
661         return values;
662     }
663 
664     /**
665      * Check location permission with same policy as {@link TelephonyManager#getServiceState()}
666      * which enforces location permission check starting from Q.
667      */
hasLocationPermission()668     private boolean hasLocationPermission() {
669         LocationAccessPolicy.LocationPermissionResult locationPermissionResult =
670                 LocationAccessPolicy.checkLocationPermission(getContext(),
671                         new LocationAccessPolicy.LocationPermissionQuery.Builder()
672                                 .setCallingPackage(getCallingPackage())
673                                 .setCallingFeatureId(getCallingAttributionTag())
674                                 .setCallingPid(Binder.getCallingPid())
675                                 .setCallingUid(Binder.getCallingUid())
676                                 .setMethod("ServiceStateProvider#query")
677                                 .setLogAsInfo(true)
678                                 .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
679                                 .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
680                                 .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
681                                 .build());
682         return locationPermissionResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
683     }
684 
685     // Return a copy of ServiceState with all sensitive info redacted.
686     @VisibleForTesting
getLocationRedactedServiceState(ServiceState serviceState)687     /* package */ static ServiceState getLocationRedactedServiceState(ServiceState serviceState) {
688         ServiceState ss =
689                 serviceState.createLocationInfoSanitizedCopy(true /*removeCoarseLocation*/);
690         // TODO(b/188061647): remove the additional redaction once it is fixed in SS
691         ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID, ServiceState.UNKNOWN_ID);
692         return ss;
693     }
694 }
695