1// Copyright 2008, The Android Open Source Project
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6//  1. Redistributions of source code must retain the above copyright notice,
7//     this list of conditions and the following disclaimer.
8//  2. Redistributions in binary form must reproduce the above copyright notice,
9//     this list of conditions and the following disclaimer in the documentation
10//     and/or other materials provided with the distribution.
11//  3. Neither the name of Google Inc. nor the names of its contributors may be
12//     used to endorse or promote products derived from this software without
13//     specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26package android.webkit.gears;
27
28import android.content.Context;
29import android.telephony.CellLocation;
30import android.telephony.ServiceState;
31import android.telephony.SignalStrength;
32import android.telephony.gsm.GsmCellLocation;
33import android.telephony.PhoneStateListener;
34import android.telephony.TelephonyManager;
35import android.util.Log;
36import android.webkit.WebView;
37
38/**
39 * Radio data provider implementation for Android.
40 */
41public final class AndroidRadioDataProvider extends PhoneStateListener {
42
43  /** Logging tag */
44  private static final String TAG = "Gears-J-RadioProvider";
45
46  /** Network types */
47  private static final int RADIO_TYPE_UNKNOWN = 0;
48  private static final int RADIO_TYPE_GSM = 1;
49  private static final int RADIO_TYPE_WCDMA = 2;
50  private static final int RADIO_TYPE_CDMA = 3;
51  private static final int RADIO_TYPE_EVDO = 4;
52  private static final int RADIO_TYPE_1xRTT = 5;
53
54  /** Simple container for radio data */
55  public static final class RadioData {
56    public int cellId = -1;
57    public int locationAreaCode = -1;
58    // TODO: use new SignalStrength instead of asu
59    public int signalStrength = -1;
60    public int mobileCountryCode = -1;
61    public int mobileNetworkCode = -1;
62    public int homeMobileCountryCode = -1;
63    public int homeMobileNetworkCode = -1;
64    public int radioType = RADIO_TYPE_UNKNOWN;
65    public String carrierName;
66
67    /**
68     * Constructs radioData object from the given telephony data.
69     * @param telephonyManager contains the TelephonyManager instance.
70     * @param cellLocation contains information about the current GSM cell.
71     * @param signalStrength is the strength of the network signal.
72     * @param serviceState contains information about the network service.
73     * @return a new RadioData object populated with the currently
74     *         available network information or null if there isn't
75     *         enough information.
76     */
77    public static RadioData getInstance(TelephonyManager telephonyManager,
78        CellLocation cellLocation, int signalStrength,
79        ServiceState serviceState) {
80
81      if (!(cellLocation instanceof GsmCellLocation)) {
82        // This also covers the case when cellLocation is null.
83        // When that happens, we do not bother creating a
84        // RadioData instance.
85        return null;
86      }
87
88      RadioData radioData = new RadioData();
89      GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
90
91      // Extract the cell id, LAC, and signal strength.
92      radioData.cellId = gsmCellLocation.getCid();
93      radioData.locationAreaCode = gsmCellLocation.getLac();
94      radioData.signalStrength = signalStrength;
95
96      // Extract the home MCC and home MNC.
97      String operator = telephonyManager.getSimOperator();
98      radioData.setMobileCodes(operator, true);
99
100      if (serviceState != null) {
101        // Extract the carrier name.
102        radioData.carrierName = serviceState.getOperatorAlphaLong();
103
104        // Extract the MCC and MNC.
105        operator = serviceState.getOperatorNumeric();
106        radioData.setMobileCodes(operator, false);
107      }
108
109      // Finally get the radio type.
110      //TODO We have to edit the parameter for getNetworkType regarding CDMA
111      int type = telephonyManager.getNetworkType();
112      if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
113        radioData.radioType = RADIO_TYPE_WCDMA;
114      } else if (type == TelephonyManager.NETWORK_TYPE_GPRS
115                 || type == TelephonyManager.NETWORK_TYPE_EDGE) {
116        radioData.radioType = RADIO_TYPE_GSM;
117      } else if (type == TelephonyManager.NETWORK_TYPE_CDMA) {
118          radioData.radioType = RADIO_TYPE_CDMA;
119      } else if (type == TelephonyManager.NETWORK_TYPE_EVDO_0) {
120          radioData.radioType = RADIO_TYPE_EVDO;
121      } else if (type == TelephonyManager.NETWORK_TYPE_EVDO_A) {
122          radioData.radioType = RADIO_TYPE_EVDO;
123      } else if (type == TelephonyManager.NETWORK_TYPE_1xRTT) {
124          radioData.radioType = RADIO_TYPE_1xRTT;
125      }
126
127      // Print out what we got.
128      Log.i(TAG, "Got the following data:");
129      Log.i(TAG, "CellId: " + radioData.cellId);
130      Log.i(TAG, "LAC: " + radioData.locationAreaCode);
131      Log.i(TAG, "MNC: " + radioData.mobileNetworkCode);
132      Log.i(TAG, "MCC: " + radioData.mobileCountryCode);
133      Log.i(TAG, "home MNC: " + radioData.homeMobileNetworkCode);
134      Log.i(TAG, "home MCC: " + radioData.homeMobileCountryCode);
135      Log.i(TAG, "Signal strength: " + radioData.signalStrength);
136      Log.i(TAG, "Carrier: " + radioData.carrierName);
137      Log.i(TAG, "Network type: " + radioData.radioType);
138
139      return radioData;
140    }
141
142    private RadioData() {}
143
144    /**
145     * Parses a string containing a mobile country code and a mobile
146     * network code and sets the corresponding member variables.
147     * @param codes is the string to parse.
148     * @param homeValues flags whether the codes are for the home operator.
149     */
150    private void setMobileCodes(String codes, boolean homeValues) {
151      if (codes != null) {
152        try {
153          // The operator numeric format is 3 digit country code plus 2 or
154          // 3 digit network code.
155          int mcc = Integer.parseInt(codes.substring(0, 3));
156          int mnc = Integer.parseInt(codes.substring(3));
157          if (homeValues) {
158            homeMobileCountryCode = mcc;
159            homeMobileNetworkCode = mnc;
160          } else {
161            mobileCountryCode = mcc;
162            mobileNetworkCode = mnc;
163          }
164        } catch (IndexOutOfBoundsException ex) {
165          Log.e(
166              TAG,
167              "AndroidRadioDataProvider: Invalid operator numeric data: " + ex);
168        } catch (NumberFormatException ex) {
169          Log.e(
170              TAG,
171              "AndroidRadioDataProvider: Operator numeric format error: " + ex);
172        }
173      }
174    }
175  };
176
177  /** The native object ID */
178  private long nativeObject;
179
180  /** The last known cellLocation */
181  private CellLocation cellLocation = null;
182
183  /** The last known signal strength */
184  // TODO: use new SignalStrength instead of asu
185  private int signalStrength = -1;
186
187  /** The last known serviceState */
188  private ServiceState serviceState = null;
189
190  /**
191   * Our TelephonyManager instance.
192   */
193  private TelephonyManager telephonyManager;
194
195  /**
196   * Public constructor. Uses the webview to get the Context object.
197   */
198  public AndroidRadioDataProvider(WebView webview, long object) {
199    super();
200    nativeObject = object;
201    telephonyManager = (TelephonyManager) webview.getContext().getSystemService(
202        Context.TELEPHONY_SERVICE);
203    if (telephonyManager == null) {
204      Log.e(TAG,
205          "AndroidRadioDataProvider: could not get tepephony manager.");
206      throw new NullPointerException(
207          "AndroidRadioDataProvider: telephonyManager is null.");
208    }
209
210    // Register for cell id, signal strength and service state changed
211    // notifications.
212    telephonyManager.listen(this, PhoneStateListener.LISTEN_CELL_LOCATION
213        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
214        | PhoneStateListener.LISTEN_SERVICE_STATE);
215  }
216
217  /**
218   * Should be called when the provider is no longer needed.
219   */
220  public void shutdown() {
221    telephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
222    Log.i(TAG, "AndroidRadioDataProvider shutdown.");
223  }
224
225  @Override
226  public void onServiceStateChanged(ServiceState state) {
227    serviceState = state;
228    notifyListeners();
229  }
230
231  @Override
232  public void onSignalStrengthsChanged(SignalStrength ss) {
233    int gsmSignalStrength = ss.getGsmSignalStrength();
234    signalStrength = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
235    notifyListeners();
236  }
237
238  @Override
239  public void onCellLocationChanged(CellLocation location) {
240    cellLocation = location;
241    notifyListeners();
242  }
243
244  private void notifyListeners() {
245    RadioData radioData = RadioData.getInstance(telephonyManager, cellLocation,
246        signalStrength, serviceState);
247    if (radioData != null) {
248      onUpdateAvailable(radioData, nativeObject);
249    }
250  }
251
252  /**
253   * The native method called when new radio data is available.
254   * @param radioData is the RadioData instance to pass to the native side.
255   * @param nativeObject is a pointer to the corresponding
256   * AndroidRadioDataProvider C++ instance.
257   */
258  private static native void onUpdateAvailable(
259      RadioData radioData, long nativeObject);
260}
261