Home | History | Annotate | Download | only in telephony
      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
    106     public boolean onCreate() {
    107         return true;
    108     }
    109 
    110     @VisibleForTesting
    111     public ServiceState getServiceState(int subId) {
    112         return mServiceStates.get(subId);
    113     }
    114 
    115     @VisibleForTesting
    116     public int getDefaultSubId() {
    117         return SubscriptionController.getInstance().getDefaultSubId();
    118     }
    119 
    120     @Override
    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
    178     public int delete(Uri uri, String selection, String[] selectionArgs) {
    179         throw new RuntimeException("Not supported");
    180     }
    181 
    182     @Override
    183     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    184         throw new RuntimeException("Not supported");
    185     }
    186 
    187     @Override
    188     public String getType(Uri uri) {
    189         throw new RuntimeException("Not supported");
    190     }
    191 
    192     @Override
    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 
    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
    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 
    334     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
    335         return oldSS.getVoiceRegState() != newSS.getVoiceRegState();
    336     }
    337 
    338     private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
    339         return oldSS.getDataRegState() != newSS.getDataRegState();
    340     }
    341 
    342     private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
    343         return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
    344     }
    345 
    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
    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