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