1 /*
2  * Copyright 2020 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.google.android.iwlan;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.Looper;
29 import android.support.annotation.IntDef;
30 import android.support.annotation.NonNull;
31 import android.telephony.CellInfo;
32 import android.telephony.SubscriptionManager;
33 import android.telephony.TelephonyCallback;
34 import android.telephony.TelephonyManager;
35 import android.telephony.ims.ImsManager;
36 import android.telephony.ims.ImsMmTelManager;
37 import android.util.Log;
38 import android.util.SparseArray;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import com.google.android.iwlan.flags.FeatureFlags;
43 import com.google.android.iwlan.flags.FeatureFlagsImpl;
44 
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Objects;
49 import java.util.Set;
50 import java.util.concurrent.ConcurrentHashMap;
51 
52 public class IwlanEventListener {
53 
54     private final FeatureFlags mFeatureFlags;
55     public static final int UNKNOWN_EVENT = -1;
56 
57     /** On {@link IwlanCarrierConfigChangeListener#onCarrierConfigChanged} is called. */
58     public static final int CARRIER_CONFIG_CHANGED_EVENT = 1;
59 
60     /** Wifi turned off or disabled. */
61     public static final int WIFI_DISABLE_EVENT = 2;
62 
63     /** Airplane mode turned off or disabled. */
64     public static final int APM_DISABLE_EVENT = 3;
65     /** Airplane mode turned on or enabled */
66     public static final int APM_ENABLE_EVENT = 4;
67 
68     /** Wifi AccessPoint changed. */
69     public static final int WIFI_AP_CHANGED_EVENT = 5;
70 
71     /** Wifi calling turned on or enabled */
72     public static final int WIFI_CALLING_ENABLE_EVENT = 6;
73 
74     /** Wifi calling turned off or disabled */
75     public static final int WIFI_CALLING_DISABLE_EVENT = 7;
76 
77     /** Cross sim calling enabled */
78     public static final int CROSS_SIM_CALLING_ENABLE_EVENT = 8;
79 
80     /** Cross sim calling disabled */
81     public static final int CROSS_SIM_CALLING_DISABLE_EVENT = 9;
82 
83     /**
84      * On {@link IwlanCarrierConfigChangeListener#onCarrierConfigChanged} is called with
85      * UNKNOWN_CARRIER_ID.
86      */
87     public static final int CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT = 10;
88 
89     /** On Cellinfo changed */
90     public static final int CELLINFO_CHANGED_EVENT = 11;
91 
92     /** On Call state changed */
93     public static final int CALL_STATE_CHANGED_EVENT = 12;
94 
95     /** On Preferred Network Type changed */
96     public static final int PREFERRED_NETWORK_TYPE_CHANGED_EVENT = 13;
97 
98     /* Events used and handled by IwlanDataService internally */
99     public static final int DATA_SERVICE_INTERNAL_EVENT_BASE = 100;
100 
101     /* Events used and handled by IwlanNetworkService internally */
102     public static final int NETWORK_SERVICE_INTERNAL_EVENT_BASE = 200;
103 
104     @IntDef({
105         CARRIER_CONFIG_CHANGED_EVENT,
106         WIFI_DISABLE_EVENT,
107         APM_DISABLE_EVENT,
108         APM_ENABLE_EVENT,
109         WIFI_AP_CHANGED_EVENT,
110         WIFI_CALLING_ENABLE_EVENT,
111         WIFI_CALLING_DISABLE_EVENT,
112         CROSS_SIM_CALLING_ENABLE_EVENT,
113         CROSS_SIM_CALLING_DISABLE_EVENT,
114         CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT,
115         CELLINFO_CHANGED_EVENT,
116         CALL_STATE_CHANGED_EVENT,
117         PREFERRED_NETWORK_TYPE_CHANGED_EVENT,
118     })
119     @interface IwlanEventType {}
120 
121     private static final String LOG_TAG = IwlanEventListener.class.getSimpleName();
122 
123     private final String SUB_TAG;
124 
125     private static Boolean sIsAirplaneModeOn;
126 
127     private static String sWifiSSID = "";
128 
129     private static final Map<Integer, IwlanEventListener> mInstances = new ConcurrentHashMap<>();
130 
131     private final Context mContext;
132     private final int mSlotId;
133     private int mSubId;
134     private Uri mCrossSimCallingUri;
135     private Uri mWfcEnabledUri;
136     private UserSettingContentObserver mUserSettingContentObserver;
137     private RadioInfoTelephonyCallback mTelephonyCallback;
138 
139     SparseArray<Set<Handler>> eventHandlers = new SparseArray<>();
140 
141     private class UserSettingContentObserver extends ContentObserver {
UserSettingContentObserver(Handler h)142         UserSettingContentObserver(Handler h) {
143             super(h);
144         }
145 
146         @Override
onChange(boolean selfChange, Uri uri)147         public void onChange(boolean selfChange, Uri uri) {
148             Objects.requireNonNull(mCrossSimCallingUri, "CrossSimCallingUri must not be null");
149             Objects.requireNonNull(mWfcEnabledUri, "WfcEnabledUri must not be null");
150             if (mCrossSimCallingUri.equals(uri)) {
151                 notifyCurrentSetting(uri);
152             } else if (mWfcEnabledUri.equals(uri)) {
153                 notifyCurrentSetting(uri);
154             }
155         }
156     }
157 
158     private class RadioInfoTelephonyCallback extends TelephonyCallback
159             implements TelephonyCallback.CellInfoListener,
160                     TelephonyCallback.CallStateListener,
161                     TelephonyCallback.AllowedNetworkTypesListener {
162         @Override
onCellInfoChanged(List<CellInfo> arrayCi)163         public void onCellInfoChanged(List<CellInfo> arrayCi) {
164             Log.d(LOG_TAG, "Cellinfo changed");
165 
166             for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
167                 IwlanEventListener instance = entry.getValue();
168                 if (instance != null) {
169                     instance.updateHandlers(arrayCi);
170                 }
171             }
172         }
173 
174         @Override
onCallStateChanged(int state)175         public void onCallStateChanged(int state) {
176             Log.d(
177                     LOG_TAG,
178                     "Call state changed to " + callStateToString(state) + " for slot " + mSlotId);
179 
180             IwlanEventListener instance = mInstances.get(mSlotId);
181             if (instance != null) {
182                 instance.updateHandlers(CALL_STATE_CHANGED_EVENT, state);
183             }
184         }
185 
186         @Override
onAllowedNetworkTypesChanged( @elephonyManager.AllowedNetworkTypesReason int reason, @TelephonyManager.NetworkTypeBitMask long allowedNetworkType)187         public void onAllowedNetworkTypesChanged(
188                 @TelephonyManager.AllowedNetworkTypesReason int reason,
189                 @TelephonyManager.NetworkTypeBitMask long allowedNetworkType) {
190             if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) {
191                 return;
192             }
193 
194             IwlanEventListener instance = mInstances.get(mSlotId);
195             if (instance != null) {
196                 instance.updateHandlers(PREFERRED_NETWORK_TYPE_CHANGED_EVENT, allowedNetworkType);
197             }
198         }
199     }
200 
201     /**
202      * Returns IwlanEventListener instance
203      */
getInstance(@onNull Context context, int slotId)204     public static IwlanEventListener getInstance(@NonNull Context context, int slotId) {
205         return mInstances.computeIfAbsent(
206                 slotId, k -> new IwlanEventListener(context, slotId, new FeatureFlagsImpl()));
207     }
208 
209     @VisibleForTesting
resetAllInstances()210     public static void resetAllInstances() {
211         mInstances.clear();
212     }
213 
214     /**
215      * Adds handler for the list of events.
216      *
217      * @param events lists of events for which the handler needs to be notified.
218      * @param handler handler to be called when the events happen
219      */
addEventListener(List<Integer> events, Handler handler)220     public synchronized void addEventListener(List<Integer> events, Handler handler) {
221         for (@IwlanEventType int event : events) {
222             if (eventHandlers.contains(event)) {
223                 eventHandlers.get(event).add(handler);
224             } else {
225                 Set<Handler> handlers = new HashSet<>();
226                 handlers.add(handler);
227                 eventHandlers.append(event, handlers);
228             }
229         }
230     }
231 
232     /**
233      * Removes handler for the list of events.
234      *
235      * @param events lists of events for which the handler needs to be removed.
236      * @param handler handler to be removed
237      */
removeEventListener(List<Integer> events, Handler handler)238     public synchronized void removeEventListener(List<Integer> events, Handler handler) {
239         for (int event : events) {
240             if (eventHandlers.contains(event)) {
241                 Set<Handler> handlers = eventHandlers.get(event);
242                 handlers.remove(handler);
243                 if (handlers.isEmpty()) {
244                     eventHandlers.delete(event);
245                 }
246             }
247         }
248         if (eventHandlers.size() == 0) {
249             mInstances.remove(mSlotId, this);
250         }
251     }
252 
253     /**
254      * Removes handler for all events it is registered
255      *
256      * @param handler handler to be removed
257      */
removeEventListener(Handler handler)258     public synchronized void removeEventListener(Handler handler) {
259         for (int i = 0; i < eventHandlers.size(); i++) {
260             Set<Handler> handlers = eventHandlers.valueAt(i);
261             handlers.remove(handler);
262             if (handlers.isEmpty()) {
263                 eventHandlers.delete(eventHandlers.keyAt(i));
264                 i--;
265             }
266         }
267         if (eventHandlers.size() == 0) {
268             mInstances.remove(mSlotId, this);
269         }
270     }
271 
272     /**
273      * Report a Broadcast received. Mainly used by IwlanBroadcastReceiver to report the following
274      * broadcasts: ACTION_AIRPLANE_MODE_CHANGED, WIFI_STATE_CHANGED_ACTION
275      *
276      * @param intent intent
277      */
onBroadcastReceived(Intent intent)278     public static synchronized void onBroadcastReceived(Intent intent) {
279         int event = UNKNOWN_EVENT;
280         switch (intent.getAction()) {
281             case Intent.ACTION_AIRPLANE_MODE_CHANGED:
282                 Boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
283                 if (sIsAirplaneModeOn != null && sIsAirplaneModeOn.equals(isAirplaneModeOn)) {
284                     // no change in apm state
285                     break;
286                 }
287                 sIsAirplaneModeOn = isAirplaneModeOn;
288                 event = sIsAirplaneModeOn ? APM_ENABLE_EVENT : APM_DISABLE_EVENT;
289                 for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
290                     IwlanEventListener instance = entry.getValue();
291                     instance.updateHandlers(event);
292                 }
293                 break;
294             case WifiManager.WIFI_STATE_CHANGED_ACTION:
295                 int wifiState =
296                         intent.getIntExtra(
297                                 WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
298                 if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
299                     event = WIFI_DISABLE_EVENT;
300                     for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
301                         IwlanEventListener instance = entry.getValue();
302                         instance.updateHandlers(event);
303                     }
304                 }
305                 break;
306         }
307     }
308 
309     /**
310      * Broadcast WIFI_AP_CHANGED_EVENT if Wifi SSID changed after Wifi connected.
311      *
312      * @param wifiInfo connected Wifi network's information.
313      */
onWifiConnected(WifiInfo wifiInfo)314     public static void onWifiConnected(WifiInfo wifiInfo) {
315         if (wifiInfo == null) {
316             Log.e(LOG_TAG, "wifiInfo is null");
317             return;
318         }
319         String wifiSSID = wifiInfo.getSSID();
320         if (wifiSSID.equals(WifiManager.UNKNOWN_SSID)) {
321             Log.e(LOG_TAG, "Could not get Wifi SSID");
322             return;
323         }
324 
325         // Check sWifiSSID is greater than 0 to avoid trigger event after device first camps on
326         // Wifi.
327         if (!sWifiSSID.isEmpty() && !sWifiSSID.equals(wifiSSID)) {
328             Log.d(LOG_TAG, "Wifi SSID changed");
329             for (Map.Entry<Integer, IwlanEventListener> entry : mInstances.entrySet()) {
330                 IwlanEventListener instance = entry.getValue();
331                 if (instance != null) {
332                     instance.updateHandlers(WIFI_AP_CHANGED_EVENT);
333                 }
334             }
335         }
336         sWifiSSID = wifiSSID;
337     }
338 
339     /**
340      * Report Carrier Config changed. Mainly used by IwlanCarrierConfigChangeListener.
341      *
342      * @param context context
343      * @param slotId slot id which carrier config is changed
344      * @param subId sub id which carrier config is changed
345      * @param carrierId carrier id
346      */
onCarrierConfigChanged( Context context, int slotId, int subId, int carrierId)347     public static synchronized void onCarrierConfigChanged(
348             Context context, int slotId, int subId, int carrierId) {
349         getInstance(context, slotId).onCarrierConfigChanged(subId, carrierId);
350     }
351 
352     /**
353      * Returns the Event id of the String. String that matches the name of the event
354      *
355      * @param event String form of the event.
356      */
getUnthrottlingEvent(String event)357     public static int getUnthrottlingEvent(String event) {
358         int ret = UNKNOWN_EVENT;
359         switch (event) {
360             case "CARRIER_CONFIG_CHANGED_EVENT":
361                 ret = CARRIER_CONFIG_CHANGED_EVENT;
362                 break;
363             case "WIFI_DISABLE_EVENT":
364                 ret = WIFI_DISABLE_EVENT;
365                 break;
366             case "APM_DISABLE_EVENT":
367                 ret = APM_DISABLE_EVENT;
368                 break;
369             case "APM_ENABLE_EVENT":
370                 ret = APM_ENABLE_EVENT;
371                 break;
372             case "WIFI_AP_CHANGED_EVENT":
373                 ret = WIFI_AP_CHANGED_EVENT;
374                 break;
375             case "WIFI_CALLING_ENABLE_EVENT":
376                 ret = WIFI_CALLING_ENABLE_EVENT;
377                 break;
378             case "WIFI_CALLING_DISABLE_EVENT":
379                 ret = WIFI_CALLING_DISABLE_EVENT;
380                 break;
381             case "CROSS_SIM_CALLING_ENABLE_EVENT":
382                 ret = CROSS_SIM_CALLING_ENABLE_EVENT;
383                 break;
384             case "CROSS_SIM_CALLING_DISABLE_EVENT":
385                 ret = CROSS_SIM_CALLING_DISABLE_EVENT;
386                 break;
387             case "CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT":
388                 ret = CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT;
389                 break;
390             case "CELLINFO_CHANGED_EVENT":
391                 ret = CELLINFO_CHANGED_EVENT;
392                 break;
393             case "PREFERRED_NETWORK_TYPE_CHANGED_EVENT":
394                 ret = PREFERRED_NETWORK_TYPE_CHANGED_EVENT;
395                 break;
396         }
397         return ret;
398     }
399 
IwlanEventListener(Context context, int slotId, FeatureFlags featureFlags)400     IwlanEventListener(Context context, int slotId, FeatureFlags featureFlags) {
401         mContext = context;
402         mSlotId = slotId;
403         mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
404         SUB_TAG = IwlanEventListener.class.getSimpleName() + "[" + slotId + "]";
405         sIsAirplaneModeOn = null;
406         mFeatureFlags = featureFlags;
407     }
408 
onCarrierConfigChanged(int subId, int carrierId)409     private void onCarrierConfigChanged(int subId, int carrierId) {
410         Log.d(SUB_TAG, "onCarrierConfigChanged");
411         if (subId != mSubId) {
412             unregisterContentObserver();
413             mSubId = subId;
414             registerContentObserver();
415         }
416         notifyCurrentSetting(mCrossSimCallingUri);
417         notifyCurrentSetting(mWfcEnabledUri);
418 
419         int event;
420         if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
421             event = CARRIER_CONFIG_CHANGED_EVENT;
422             registerTelephonyCallback();
423         } else {
424             event = CARRIER_CONFIG_UNKNOWN_CARRIER_EVENT;
425         }
426         updateHandlers(event);
427     }
428 
429     /** Unregister ContentObserver. */
unregisterContentObserver()430     void unregisterContentObserver() {
431         if (mUserSettingContentObserver != null) {
432             mContext.getContentResolver().unregisterContentObserver(mUserSettingContentObserver);
433         }
434         mCrossSimCallingUri = null;
435         mWfcEnabledUri = null;
436     }
437 
438     /** Initiate ContentObserver if it is not created. And, register it with the current sub id. */
registerContentObserver()439     private void registerContentObserver() {
440         if (mUserSettingContentObserver == null) {
441             HandlerThread userSettingHandlerThread =
442                     new HandlerThread(IwlanNetworkService.class.getSimpleName());
443             userSettingHandlerThread.start();
444             Looper looper = userSettingHandlerThread.getLooper();
445             Handler handler = new Handler(looper);
446             mUserSettingContentObserver = new UserSettingContentObserver(handler);
447         }
448 
449         if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
450             return;
451         }
452 
453         ContentResolver resolver = mContext.getContentResolver();
454         // Register for CrossSimCalling setting uri
455         mCrossSimCallingUri =
456                 Uri.withAppendedPath(
457                         SubscriptionManager.CROSS_SIM_ENABLED_CONTENT_URI, String.valueOf(mSubId));
458         resolver.registerContentObserver(mCrossSimCallingUri, true, mUserSettingContentObserver);
459 
460         // Register for WifiCalling setting uri
461         mWfcEnabledUri =
462                 Uri.withAppendedPath(
463                         SubscriptionManager.WFC_ENABLED_CONTENT_URI, String.valueOf(mSubId));
464         resolver.registerContentObserver(mWfcEnabledUri, true, mUserSettingContentObserver);
465     }
466 
467     @VisibleForTesting
notifyCurrentSetting(Uri uri)468     void notifyCurrentSetting(Uri uri) {
469         if (uri == null) {
470             return;
471         }
472         String uriString = uri.getPath();
473         int subIndex = Integer.parseInt(uriString.substring(uriString.lastIndexOf('/') + 1));
474         int slotIndex = SubscriptionManager.getSlotIndex(subIndex);
475 
476         if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
477             Log.e(SUB_TAG, "Invalid slot index: " + slotIndex);
478             return;
479         }
480 
481         if (uri.equals(mCrossSimCallingUri)) {
482             boolean isCstEnabled = IwlanHelper.isCrossSimCallingEnabled(mContext, slotIndex);
483             int event =
484                     (isCstEnabled)
485                             ? CROSS_SIM_CALLING_ENABLE_EVENT
486                             : CROSS_SIM_CALLING_DISABLE_EVENT;
487             getInstance(mContext, slotIndex).updateHandlers(event);
488         } else if (uri.equals(mWfcEnabledUri)) {
489             ImsManager imsManager = mContext.getSystemService(ImsManager.class);
490             if (imsManager == null) {
491                 Log.e(SUB_TAG, "Could not find  ImsManager");
492                 return;
493             }
494             ImsMmTelManager imsMmTelManager = imsManager.getImsMmTelManager(subIndex);
495             if (imsMmTelManager == null) {
496                 Log.e(SUB_TAG, "Could not find  ImsMmTelManager");
497                 return;
498             }
499             boolean wfcEnabled = false;
500             try {
501                 wfcEnabled = imsMmTelManager.isVoWiFiSettingEnabled();
502             } catch (IllegalArgumentException e) {
503                 Log.w(SUB_TAG, e.getMessage());
504             }
505             int event = (wfcEnabled) ? WIFI_CALLING_ENABLE_EVENT : WIFI_CALLING_DISABLE_EVENT;
506             getInstance(mContext, slotIndex).updateHandlers(event);
507         } else {
508             Log.e(SUB_TAG, "Unknown Uri : " + uri);
509         }
510     }
511 
512     @VisibleForTesting
registerTelephonyCallback()513     void registerTelephonyCallback() {
514         Log.d(SUB_TAG, "registerTelephonyCallback");
515         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
516         telephonyManager =
517                 Objects.requireNonNull(telephonyManager)
518                         .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
519         mTelephonyCallback = new RadioInfoTelephonyCallback();
520         telephonyManager.registerTelephonyCallback(Runnable::run, mTelephonyCallback);
521     }
522 
523     @VisibleForTesting
setCrossSimCallingUri(Uri uri)524     void setCrossSimCallingUri(Uri uri) {
525         mCrossSimCallingUri = uri;
526     }
527 
528     @VisibleForTesting
setWfcEnabledUri(Uri uri)529     void setWfcEnabledUri(Uri uri) {
530         mWfcEnabledUri = uri;
531     }
532 
533     @VisibleForTesting
getTelephonyCallback()534     RadioInfoTelephonyCallback getTelephonyCallback() {
535         return mTelephonyCallback;
536     }
537 
updateHandlers(int event)538     private synchronized void updateHandlers(int event) {
539         if (eventHandlers.contains(event)) {
540             Log.d(SUB_TAG, "Updating handlers for the event: " + event);
541             for (Handler handler : eventHandlers.get(event)) {
542                 handler.obtainMessage(event, mSlotId, 0 /* unused */).sendToTarget();
543             }
544         }
545     }
546 
updateHandlers(List<CellInfo> arrayCi)547     private synchronized void updateHandlers(List<CellInfo> arrayCi) {
548         int event = IwlanEventListener.CELLINFO_CHANGED_EVENT;
549         if (eventHandlers.contains(event)) {
550             Log.d(SUB_TAG, "Updating handlers for the event: " + event);
551             for (Handler handler : eventHandlers.get(event)) {
552                 handler.obtainMessage(event, mSlotId, 0 /* unused */, arrayCi).sendToTarget();
553             }
554         }
555     }
556 
updateHandlers(int event, int state)557     private synchronized void updateHandlers(int event, int state) {
558         if (eventHandlers.contains(event)) {
559             Log.d(SUB_TAG, "Updating handlers for the event: " + event);
560             for (Handler handler : eventHandlers.get(event)) {
561                 handler.obtainMessage(event, mSlotId, state).sendToTarget();
562             }
563         }
564     }
565 
updateHandlers(int event, long allowedNetworkType)566     private synchronized void updateHandlers(int event, long allowedNetworkType) {
567         if (eventHandlers.contains(event)) {
568             Log.d(SUB_TAG, "Updating handlers for the event: " + event);
569             for (Handler handler : eventHandlers.get(event)) {
570                 handler.obtainMessage(event, mSlotId, 0 /* unused */, allowedNetworkType)
571                         .sendToTarget();
572             }
573         }
574     }
575 
callStateToString(int state)576     private String callStateToString(int state) {
577         switch (state) {
578             case TelephonyManager.CALL_STATE_IDLE:
579                 return "CALL_STATE_IDLE";
580             case TelephonyManager.CALL_STATE_RINGING:
581                 return "CALL_STATE_RINGING";
582             case TelephonyManager.CALL_STATE_OFFHOOK:
583                 return "CALL_STATE_OFFHOOK";
584             default:
585                 return "Unknown Call State (" + state + ")";
586         }
587     }
588 }
589