• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.provider.Telephony.ServiceStateTable;
21 import static android.provider.Telephony.ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR;
22 import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_INDEX;
23 import static android.provider.Telephony.ServiceStateTable.CDMA_ERI_ICON_MODE;
24 import static android.provider.Telephony.ServiceStateTable.CDMA_ROAMING_INDICATOR;
25 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
26 import static android.provider.Telephony.ServiceStateTable.CSS_INDICATOR;
27 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_LONG;
28 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT;
29 import static android.provider.Telephony.ServiceStateTable.DATA_OPERATOR_NUMERIC;
30 import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
31 import static android.provider.Telephony.ServiceStateTable.DATA_ROAMING_TYPE;
32 import static android.provider.Telephony.ServiceStateTable.IS_EMERGENCY_ONLY;
33 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
34 import static android.provider.Telephony.ServiceStateTable.IS_USING_CARRIER_AGGREGATION;
35 import static android.provider.Telephony.ServiceStateTable.NETWORK_ID;
36 import static android.provider.Telephony.ServiceStateTable.OPERATOR_ALPHA_LONG_RAW;
37 import static android.provider.Telephony.ServiceStateTable.OPERATOR_ALPHA_SHORT_RAW;
38 import static android.provider.Telephony.ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY;
39 import static android.provider.Telephony.ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY;
40 import static android.provider.Telephony.ServiceStateTable.SERVICE_STATE;
41 import static android.provider.Telephony.ServiceStateTable.SYSTEM_ID;
42 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG;
43 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT;
44 import static android.provider.Telephony.ServiceStateTable.VOICE_OPERATOR_NUMERIC;
45 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
46 import static android.provider.Telephony.ServiceStateTable.VOICE_ROAMING_TYPE;
47 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
48 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
49 
50 import android.content.ContentProvider;
51 import android.content.ContentValues;
52 import android.content.Context;
53 import android.database.Cursor;
54 import android.database.MatrixCursor;
55 import android.database.MatrixCursor.RowBuilder;
56 import android.net.Uri;
57 import android.os.Parcel;
58 import android.telephony.ServiceState;
59 import android.telephony.SubscriptionManager;
60 import android.util.Log;
61 
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.telephony.SubscriptionController;
64 
65 import java.util.HashMap;
66 
67 
68 public class ServiceStateProvider extends ContentProvider {
69     private static final String TAG = "ServiceStateProvider";
70 
71     public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
72     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
73 
74     private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
75     private static final String[] sColumns = {
76         VOICE_REG_STATE,
77         DATA_REG_STATE,
78         VOICE_ROAMING_TYPE,
79         DATA_ROAMING_TYPE,
80         VOICE_OPERATOR_ALPHA_LONG,
81         VOICE_OPERATOR_ALPHA_SHORT,
82         VOICE_OPERATOR_NUMERIC,
83         DATA_OPERATOR_ALPHA_LONG,
84         DATA_OPERATOR_ALPHA_SHORT,
85         DATA_OPERATOR_NUMERIC,
86         IS_MANUAL_NETWORK_SELECTION,
87         RIL_VOICE_RADIO_TECHNOLOGY,
88         RIL_DATA_RADIO_TECHNOLOGY,
89         CSS_INDICATOR,
90         NETWORK_ID,
91         SYSTEM_ID,
92         CDMA_ROAMING_INDICATOR,
93         CDMA_DEFAULT_ROAMING_INDICATOR,
94         CDMA_ERI_ICON_INDEX,
95         CDMA_ERI_ICON_MODE,
96         IS_EMERGENCY_ONLY,
97         IS_USING_CARRIER_AGGREGATION,
98         OPERATOR_ALPHA_LONG_RAW,
99         OPERATOR_ALPHA_SHORT_RAW,
100     };
101 
102     @Override
onCreate()103     public boolean onCreate() {
104         return true;
105     }
106 
107     @VisibleForTesting
getServiceState(int subId)108     public ServiceState getServiceState(int subId) {
109         return mServiceStates.get(subId);
110     }
111 
112     @VisibleForTesting
getDefaultSubId()113     public int getDefaultSubId() {
114         return SubscriptionController.getInstance().getDefaultSubId();
115     }
116 
117     @Override
insert(Uri uri, ContentValues values)118     public Uri insert(Uri uri, ContentValues values) {
119         if (uri.isPathPrefixMatch(CONTENT_URI)) {
120             // Parse the subId
121             int subId = 0;
122             try {
123                 subId = Integer.parseInt(uri.getLastPathSegment());
124             } catch (NumberFormatException e) {
125                 Log.e(TAG, "insert: no subId provided in uri");
126                 throw e;
127             }
128             Log.d(TAG, "subId=" + subId);
129 
130             // handle DEFAULT_SUBSCRIPTION_ID
131             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
132                 subId = getDefaultSubId();
133             }
134 
135             final Parcel p = Parcel.obtain();
136             final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
137             p.unmarshall(rawBytes, 0, rawBytes.length);
138             p.setDataPosition(0);
139 
140             // create the new service state
141             final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
142 
143             // notify listeners
144             // if ss is null (e.g. first service state update) we will notify for all fields
145             ServiceState ss = getServiceState(subId);
146             notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
147             notifyChangeForSubId(getContext(), ss, newSS, subId);
148 
149             // store the new service state
150             mServiceStates.put(subId, newSS);
151             return uri;
152         }
153         return null;
154     }
155 
156     @Override
delete(Uri uri, String selection, String[] selectionArgs)157     public int delete(Uri uri, String selection, String[] selectionArgs) {
158         throw new RuntimeException("Not supported");
159     }
160 
161     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)162     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
163         throw new RuntimeException("Not supported");
164     }
165 
166     @Override
getType(Uri uri)167     public String getType(Uri uri) {
168         throw new RuntimeException("Not supported");
169     }
170 
171     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)172     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
173             String sortOrder) {
174         if (!uri.isPathPrefixMatch(CONTENT_URI)) {
175             throw new IllegalArgumentException("Invalid URI: " + uri);
176         } else {
177             // Parse the subId
178             int subId = 0;
179             try {
180                 subId = Integer.parseInt(uri.getLastPathSegment());
181             } catch (NumberFormatException e) {
182                 Log.d(TAG, "query: no subId provided in uri, using default.");
183                 subId = getDefaultSubId();
184             }
185             Log.d(TAG, "subId=" + subId);
186 
187             // handle DEFAULT_SUBSCRIPTION_ID
188             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
189                 subId = getDefaultSubId();
190             }
191 
192             // Get the service state
193             ServiceState ss = getServiceState(subId);
194             if (ss == null) {
195                 Log.d(TAG, "returning null");
196                 return null;
197             }
198 
199             // Build the result
200             final int voice_reg_state = ss.getVoiceRegState();
201             final int data_reg_state = ss.getDataRegState();
202             final int voice_roaming_type = ss.getVoiceRoamingType();
203             final int data_roaming_type = ss.getDataRoamingType();
204             final String voice_operator_alpha_long = ss.getVoiceOperatorAlphaLong();
205             final String voice_operator_alpha_short = ss.getVoiceOperatorAlphaShort();
206             final String voice_operator_numeric = ss.getVoiceOperatorNumeric();
207             final String data_operator_alpha_long = ss.getDataOperatorAlphaLong();
208             final String data_operator_alpha_short = ss.getDataOperatorAlphaShort();
209             final String data_operator_numeric = ss.getDataOperatorNumeric();
210             final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
211             final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
212             final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
213             final int css_indicator = ss.getCssIndicator();
214             final int network_id = ss.getCdmaNetworkId();
215             final int system_id = ss.getCdmaSystemId();
216             final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
217             final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
218             final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
219             final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
220             final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
221             final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
222             final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
223             final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
224 
225             return buildSingleRowResult(projection, sColumns, new Object[] {
226                         voice_reg_state,
227                         data_reg_state,
228                         voice_roaming_type,
229                         data_roaming_type,
230                         voice_operator_alpha_long,
231                         voice_operator_alpha_short,
232                         voice_operator_numeric,
233                         data_operator_alpha_long,
234                         data_operator_alpha_short,
235                         data_operator_numeric,
236                         is_manual_network_selection,
237                         ril_voice_radio_technology,
238                         ril_data_radio_technology,
239                         css_indicator,
240                         network_id,
241                         system_id,
242                         cdma_roaming_indicator,
243                         cdma_default_roaming_indicator,
244                         cdma_eri_icon_index,
245                         cdma_eri_icon_mode,
246                         is_emergency_only,
247                         is_using_carrier_aggregation,
248                         operator_alpha_long_raw,
249                         operator_alpha_short_raw,
250             });
251         }
252     }
253 
buildSingleRowResult(String[] projection, String[] availableColumns, Object[] data)254     private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
255             Object[] data) {
256         if (projection == null) {
257             projection = availableColumns;
258         }
259         final MatrixCursor c = new MatrixCursor(projection, 1);
260         final RowBuilder row = c.newRow();
261         for (int i = 0; i < c.getColumnCount(); i++) {
262             final String columnName = c.getColumnName(i);
263             boolean found = false;
264             for (int j = 0; j < availableColumns.length; j++) {
265                 if (availableColumns[j].equals(columnName)) {
266                     row.add(data[j]);
267                     found = true;
268                     break;
269                 }
270             }
271             if (!found) {
272                 throw new IllegalArgumentException("Invalid column " + projection[i]);
273             }
274         }
275         return c;
276     }
277 
278     /**
279      * Notify interested apps that certain fields of the ServiceState have changed.
280      *
281      * Apps which want to wake when specific fields change can use
282      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
283      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targetting version O.
284      *
285      * We will only notify for certain fields. This is an intentional change from the behavior of
286      * the broadcast. Listeners will be notified when the voice or data registration state or
287      * roaming type changes.
288      */
289     @VisibleForTesting
notifyChangeForSubIdAndField(Context context, ServiceState oldSS, ServiceState newSS, int subId)290     public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
291             ServiceState newSS, int subId) {
292         final boolean firstUpdate = (oldSS == null) ? true : false;
293 
294         // for every field, if the field has changed values, notify via the provider
295         if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
296             context.getContentResolver().notifyChange(
297                     getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
298                     /* observer= */ null, /* syncToNetwork= */ false);
299         }
300         if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
301             context.getContentResolver().notifyChange(
302                     getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
303         }
304         if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
305             context.getContentResolver().notifyChange(
306                     getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
307         }
308         if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
309             context.getContentResolver().notifyChange(
310                     getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
311         }
312     }
313 
voiceRegStateChanged(ServiceState oldSS, ServiceState newSS)314     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
315         return oldSS.getVoiceRegState() != newSS.getVoiceRegState();
316     }
317 
dataRegStateChanged(ServiceState oldSS, ServiceState newSS)318     private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
319         return oldSS.getDataRegState() != newSS.getDataRegState();
320     }
321 
voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)322     private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
323         return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
324     }
325 
dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS)326     private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
327         return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
328     }
329 
330     /**
331      * Notify interested apps that the ServiceState has changed.
332      *
333      * Apps which want to wake when any field in the ServiceState has changed can use
334      * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
335      * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
336      *
337      * We will only notify for certain fields. This is an intentional change from the behavior of
338      * the broadcast. Listeners will only be notified when the voice/data registration state or
339      * roaming type changes.
340      */
341     @VisibleForTesting
notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS, int subId)342     public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
343             int subId) {
344         // if the voice or data registration or roaming state field has changed values, notify via
345         // the provider.
346         // If oldSS is null and newSS is not (e.g. first update of service state) this will also
347         // notify
348         if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
349                 || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
350             context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
351         }
352     }
353 }
354