1 /* //device/content/providers/telephony/TelephonyProvider.java
2 **
3 ** Copyright 2016, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 package com.android.providers.telephony;
19 
20 import android.content.ContentProvider;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.database.MatrixCursor;
25 import android.database.MatrixCursor.RowBuilder;
26 import android.net.Uri;
27 import android.telephony.ServiceState;
28 import android.telephony.SubscriptionManager;
29 import android.telephony.TelephonyManager;
30 import android.util.Log;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.internal.telephony.Phone;
34 import com.android.internal.telephony.PhoneFactory;
35 import com.android.internal.telephony.SubscriptionController;
36 
37 import java.lang.NumberFormatException;
38 import java.util.HashMap;
39 import java.util.Objects;
40 
41 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
42 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
43 
44 import static android.provider.Telephony.ServiceStateTable;
45 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
46 
47 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
48 import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
49 import static android.provider.Telephony.ServiceStateTable.VOICE_ROAMING_TYPE;
50 import static android.provider.Telephony.ServiceStateTable.DATA_ROAMING_TYPE;
51 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG;
52 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT;
53 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_NUMERIC;
54 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_LONG;
55 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT;
56 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_NUMERIC;
57 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
58 import static android.provider.Telephony.ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY;
59 import static android.provider.Telephony.ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY;
60 import static android.provider.Telephony.ServiceStateTable.CSS_INDICATOR;
61 import static android.provider.Telephony.ServiceStateTable.NETWORK_ID;
62 import static android.provider.Telephony.ServiceStateTable.SYSTEM_ID;
63 import static android.provider.Telephony.ServiceStateTable.CDMA_ROAMING_INDICATOR;
64 import static android.provider.Telephony.ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR;
65 import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_INDEX;
66 import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_MODE;
67 import static android.provider.Telephony.ServiceStateTable.IS_EMERGENCY_ONLY;
68 import static android.provider.Telephony.ServiceStateTable.IS_DATA_ROAMING_FROM_REGISTRATION;
69 import static android.provider.Telephony.ServiceStateTable.IS_USING_CARRIER_AGGREGATION;
70 
71 
72 public class ServiceStateProvider extends ContentProvider {
73     private static final String TAG = "ServiceStateProvider";
74 
75     public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
76     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
77 
78     private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
79     private static final String[] sColumns = {
80         VOICE_REG_STATE,
81         DATA_REG_STATE,
82         VOICE_ROAMING_TYPE,
83         DATA_ROAMING_TYPE,
84         VOICE_OPERATOR_ALPHA_LONG,
85         VOICE_OPERATOR_ALPHA_SHORT,
86         VOICE_OPERATOR_NUMERIC,
87         DATA_OPERATOR_ALPHA_LONG,
88         DATA_OPERATOR_ALPHA_SHORT,
89         DATA_OPERATOR_NUMERIC,
90         IS_MANUAL_NETWORK_SELECTION,
91         RIL_VOICE_RADIO_TECHNOLOGY,
92         RIL_DATA_RADIO_TECHNOLOGY,
93         CSS_INDICATOR,
94         NETWORK_ID,
95         SYSTEM_ID,
96         CDMA_ROAMING_INDICATOR,
97         CDMA_DEFAULT_ROAMING_INDICATOR,
98         CDMA_ERI_ICON_INDEX,
99         CDMA_ERI_ICON_MODE,
100         IS_EMERGENCY_ONLY,
101         IS_DATA_ROAMING_FROM_REGISTRATION,
102         IS_USING_CARRIER_AGGREGATION,
103     };
104 
105     @Override
onCreate()106     public boolean onCreate() {
107         return true;
108     }
109 
110     @VisibleForTesting
getServiceState(int subId)111     public ServiceState getServiceState(int subId) {
112         return mServiceStates.get(subId);
113     }
114 
115     @VisibleForTesting
getDefaultSubId()116     public int getDefaultSubId() {
117         return SubscriptionController.getInstance().getDefaultSubId();
118     }
119 
120     @Override
insert(Uri uri, ContentValues values)121     public Uri insert(Uri uri, ContentValues values) {
122         if (uri.isPathPrefixMatch(CONTENT_URI)) {
123             // Parse the subId
124             int subId = 0;
125             try {
126                 subId = Integer.parseInt(uri.getLastPathSegment());
127             } catch (NumberFormatException e) {
128                 Log.e(TAG, "insert: no subId provided in uri");
129                 throw e;
130             }
131             Log.d(TAG, "subId=" + subId);
132 
133             // handle DEFAULT_SUBSCRIPTION_ID
134             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
135                 subId = getDefaultSubId();
136             }
137 
138             // create the new service state
139             ServiceState newSS = new ServiceState();
140             newSS.setVoiceRegState(values.getAsInteger(VOICE_REG_STATE));
141             newSS.setDataRegState(values.getAsInteger(DATA_REG_STATE));
142             newSS.setVoiceOperatorName(values.getAsString(VOICE_OPERATOR_ALPHA_LONG),
143                         values.getAsString(VOICE_OPERATOR_ALPHA_SHORT),
144                         values.getAsString(VOICE_OPERATOR_NUMERIC));
145             newSS.setDataOperatorName(values.getAsString(DATA_OPERATOR_ALPHA_LONG),
146                     values.getAsString(DATA_OPERATOR_ALPHA_SHORT),
147                     values.getAsString(DATA_OPERATOR_NUMERIC));
148             newSS.setIsManualSelection(values.getAsBoolean(IS_MANUAL_NETWORK_SELECTION));
149             newSS.setRilVoiceRadioTechnology(values.getAsInteger(RIL_VOICE_RADIO_TECHNOLOGY));
150             newSS.setRilDataRadioTechnology(values.getAsInteger(RIL_DATA_RADIO_TECHNOLOGY));
151             newSS.setCssIndicator(values.getAsInteger(CSS_INDICATOR));
152             newSS.setSystemAndNetworkId(values.getAsInteger(SYSTEM_ID),
153                     values.getAsInteger(NETWORK_ID));
154             newSS.setCdmaRoamingIndicator(values.getAsInteger(CDMA_ROAMING_INDICATOR));
155             newSS.setCdmaDefaultRoamingIndicator(
156                     values.getAsInteger(CDMA_DEFAULT_ROAMING_INDICATOR));
157             newSS.setCdmaEriIconIndex(values.getAsInteger(CDMA_ERI_ICON_INDEX));
158             newSS.setCdmaEriIconMode(values.getAsInteger(CDMA_ERI_ICON_MODE));
159             newSS.setEmergencyOnly(values.getAsBoolean(IS_EMERGENCY_ONLY));
160             newSS.setDataRoamingFromRegistration(
161                     values.getAsBoolean(IS_DATA_ROAMING_FROM_REGISTRATION));
162             newSS.setIsUsingCarrierAggregation(values.getAsBoolean(IS_USING_CARRIER_AGGREGATION));
163 
164             // notify listeners
165             // if ss is null (e.g. first service state update) we will notify for all fields
166             ServiceState ss = getServiceState(subId);
167             notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
168             notifyChangeForSubId(getContext(), ss, newSS, subId);
169 
170             // store the new service state
171             mServiceStates.put(subId, newSS);
172             return uri;
173         }
174         return null;
175     }
176 
177     @Override
delete(Uri uri, String selection, String[] selectionArgs)178     public int delete(Uri uri, String selection, String[] selectionArgs) {
179         throw new RuntimeException("Not supported");
180     }
181 
182     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)183     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
184         throw new RuntimeException("Not supported");
185     }
186 
187     @Override
getType(Uri uri)188     public String getType(Uri uri) {
189         throw new RuntimeException("Not supported");
190     }
191 
192     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)193     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
194             String sortOrder) {
195         if (!uri.isPathPrefixMatch(CONTENT_URI)) {
196             throw new IllegalArgumentException("Invalid URI: " + uri);
197         } else {
198             // Parse the subId
199             int subId = 0;
200             try {
201                 subId = Integer.parseInt(uri.getLastPathSegment());
202             } catch (NumberFormatException e) {
203                 Log.d(TAG, "query: no subId provided in uri, using default.");
204                 subId = getDefaultSubId();
205             }
206             Log.d(TAG, "subId=" + subId);
207 
208             // handle DEFAULT_SUBSCRIPTION_ID
209             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
210                 subId = getDefaultSubId();
211             }
212 
213             // Get the service state
214             ServiceState ss = getServiceState(subId);
215             if (ss == null) {
216                 Log.d(TAG, "returning null");
217                 return null;
218             }
219 
220             // Build the result
221             final int voice_reg_state = ss.getVoiceRegState();
222             final int data_reg_state = ss.getDataRegState();
223             final int voice_roaming_type = ss.getVoiceRoamingType();
224             final int data_roaming_type = ss.getDataRoamingType();
225             final String voice_operator_alpha_long = ss.getVoiceOperatorAlphaLong();
226             final String voice_operator_alpha_short = ss.getVoiceOperatorAlphaShort();
227             final String voice_operator_numeric = ss.getVoiceOperatorNumeric();
228             final String data_operator_alpha_long = ss.getDataOperatorAlphaLong();
229             final String data_operator_alpha_short = ss.getDataOperatorAlphaShort();
230             final String data_operator_numeric = ss.getDataOperatorNumeric();
231             final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
232             final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
233             final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
234             final int css_indicator = ss.getCssIndicator();
235             final int network_id = ss.getNetworkId();
236             final int system_id = ss.getSystemId();
237             final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
238             final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
239             final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
240             final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
241             final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
242             final int is_data_roaming_from_registration =
243                     (ss.getDataRoamingFromRegistration()) ? 1 : 0;
244             final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
245 
246             return buildSingleRowResult(projection, sColumns, new Object[] {
247                         voice_reg_state,
248                         data_reg_state,
249                         voice_roaming_type,
250                         data_roaming_type,
251                         voice_operator_alpha_long,
252                         voice_operator_alpha_short,
253                         voice_operator_numeric,
254                         data_operator_alpha_long,
255                         data_operator_alpha_short,
256                         data_operator_numeric,
257                         is_manual_network_selection,
258                         ril_voice_radio_technology,
259                         ril_data_radio_technology,
260                         css_indicator,
261                         network_id,
262                         system_id,
263                         cdma_roaming_indicator,
264                         cdma_default_roaming_indicator,
265                         cdma_eri_icon_index,
266                         cdma_eri_icon_mode,
267                         is_emergency_only,
268                         is_data_roaming_from_registration,
269                         is_using_carrier_aggregation,
270             });
271         }
272     }
273 
buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)274     private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
275             Object[] data) {
276         if (projection == null) {
277             projection = availableColumns;
278         }
279         final MatrixCursor c = new MatrixCursor(projection, 1);
280         final RowBuilder row = c.newRow();
281         for (int i = 0; i < c.getColumnCount(); i++) {
282             final String columnName = c.getColumnName(i);
283             boolean found = false;
284             for (int j = 0; j < availableColumns.length; j++) {
285                 if (availableColumns[j].equals(columnName)) {
286                     row.add(data[j]);
287                     found = true;
288                     break;
289                 }
290             }
291             if (!found) {
292                 throw new IllegalArgumentException("Invalid column " + projection[i]);
293             }
294         }
295         return c;
296     }
297 
298     /**
299      * Notify interested apps that certain fields of the ServiceState have changed.
300      *
301      * Apps which want to wake when specific fields change can use
302      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
303      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targetting version O.
304      *
305      * We will only notify for certain fields. This is an intentional change from the behavior of
306      * the broadcast. Listeners will be notified when the voice or data registration state or
307      * roaming type changes.
308      */
309     @VisibleForTesting
notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)310     public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
311             ServiceState newSS, int subId) {
312         final boolean firstUpdate = (oldSS == null) ? true : false;
313 
314         // for every field, if the field has changed values, notify via the provider
315         if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
316             context.getContentResolver().notifyChange(
317                     getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
318                     /* observer= */ null, /* syncToNetwork= */ false);
319         }
320         if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
321             context.getContentResolver().notifyChange(
322                     getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
323         }
324         if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
325             context.getContentResolver().notifyChange(
326                     getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
327         }
328         if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
329             context.getContentResolver().notifyChange(
330                     getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
331         }
332     }
333 
voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)334     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
335         return oldSS.getVoiceRegState() != newSS.getVoiceRegState();
336     }
337 
dataRegStateChanged(ServiceState oldSS, ServiceState newSS)338     private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
339         return oldSS.getDataRegState() != newSS.getDataRegState();
340     }
341 
voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)342     private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
343         return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
344     }
345 
dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)346     private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
347         return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
348     }
349 
350     /**
351      * Notify interested apps that the ServiceState has changed.
352      *
353      * Apps which want to wake when any field in the ServiceState has changed can use
354      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
355      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targetting version O.
356      *
357      * We will only notify for certain fields. This is an intentional change from the behavior of
358      * the broadcast. Listeners will be notified when the voice or data registration state or
359      * roaming type changes.
360      */
361     @VisibleForTesting
notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)362     public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
363             int subId) {
364         // if the voice or data registration or roaming state field has changed values, notify via
365         // the provider.
366         // If oldSS is null and newSS is not (e.g. first update of service state) this will also
367         // notify
368         if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
369                 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
370             context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
371         }
372     }
373 }
374