• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.android.phone;
18 
19 import android.content.Intent;
20 import android.net.Uri;
21 import android.os.AsyncResult;
22 import android.os.Binder;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.ServiceManager;
28 import android.telephony.NeighboringCellInfo;
29 import android.telephony.ServiceState;
30 import android.telephony.TelephonyManager;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import com.android.internal.telephony.DefaultPhoneNotifier;
35 import com.android.internal.telephony.IccCard;
36 import com.android.internal.telephony.ITelephony;
37 import com.android.internal.telephony.Phone;
38 
39 import java.util.List;
40 import java.util.ArrayList;
41 
42 /**
43  * Implementation of the ITelephony interface.
44  */
45 public class PhoneInterfaceManager extends ITelephony.Stub {
46     private static final String LOG_TAG = "PhoneInterfaceManager";
47     private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2);
48 
49     // Message codes used with mMainThreadHandler
50     private static final int CMD_HANDLE_PIN_MMI = 1;
51     private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
52     private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
53     private static final int CMD_ANSWER_RINGING_CALL = 4;
54     private static final int CMD_END_CALL = 5;  // not used yet
55     private static final int CMD_SILENCE_RINGER = 6;
56 
57     PhoneApp mApp;
58     Phone mPhone;
59     MainThreadHandler mMainThreadHandler;
60 
61     /**
62      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
63      * request after sending. The main thread will notify the request when it is complete.
64      */
65     private static final class MainThreadRequest {
66         /** The argument to use for the request */
67         public Object argument;
68         /** The result of the request that is run on the main thread */
69         public Object result;
70 
MainThreadRequest(Object argument)71         public MainThreadRequest(Object argument) {
72             this.argument = argument;
73         }
74     }
75 
76     /**
77      * A handler that processes messages on the main thread in the phone process. Since many
78      * of the Phone calls are not thread safe this is needed to shuttle the requests from the
79      * inbound binder threads to the main thread in the phone process.  The Binder thread
80      * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
81      * on, which will be notified when the operation completes and will contain the result of the
82      * request.
83      *
84      * <p>If a MainThreadRequest object is provided in the msg.obj field,
85      * note that request.result must be set to something non-null for the calling thread to
86      * unblock.
87      */
88     private final class MainThreadHandler extends Handler {
89         @Override
handleMessage(Message msg)90         public void handleMessage(Message msg) {
91             MainThreadRequest request;
92             Message onCompleted;
93             AsyncResult ar;
94 
95             switch (msg.what) {
96                 case CMD_HANDLE_PIN_MMI:
97                     request = (MainThreadRequest) msg.obj;
98                     request.result = Boolean.valueOf(
99                             mPhone.handlePinMmi((String) request.argument));
100                     // Wake up the requesting thread
101                     synchronized (request) {
102                         request.notifyAll();
103                     }
104                     break;
105 
106                 case CMD_HANDLE_NEIGHBORING_CELL:
107                     request = (MainThreadRequest) msg.obj;
108                     onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
109                             request);
110                     mPhone.getNeighboringCids(onCompleted);
111                     break;
112 
113                 case EVENT_NEIGHBORING_CELL_DONE:
114                     ar = (AsyncResult) msg.obj;
115                     request = (MainThreadRequest) ar.userObj;
116                     if (ar.exception == null && ar.result != null) {
117                         request.result = ar.result;
118                     } else {
119                         // create an empty list to notify the waiting thread
120                         request.result = new ArrayList<NeighboringCellInfo>();
121                     }
122                     // Wake up the requesting thread
123                     synchronized (request) {
124                         request.notifyAll();
125                     }
126                     break;
127 
128                 case CMD_ANSWER_RINGING_CALL:
129                     answerRingingCallInternal();
130                     break;
131 
132                 case CMD_SILENCE_RINGER:
133                     silenceRingerInternal();
134                     break;
135 
136                 case CMD_END_CALL:
137                     request = (MainThreadRequest) msg.obj;
138                     boolean hungUp = false;
139                     int phoneType = mPhone.getPhoneType();
140                     if (phoneType == Phone.PHONE_TYPE_CDMA) {
141                         // CDMA: If the user presses the Power button we treat it as
142                         // ending the complete call session
143                         hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
144                     } else if (phoneType == Phone.PHONE_TYPE_GSM) {
145                         // GSM: End the call as per the Phone state
146                         hungUp = PhoneUtils.hangup(mPhone);
147                     } else {
148                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
149                     }
150                     if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
151                     request.result = hungUp;
152                     // Wake up the requesting thread
153                     synchronized (request) {
154                         request.notifyAll();
155                     }
156                     break;
157 
158                 default:
159                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
160                     break;
161             }
162         }
163     }
164 
165     /**
166      * Posts the specified command to be executed on the main thread,
167      * waits for the request to complete, and returns the result.
168      * @see sendRequestAsync
169      */
sendRequest(int command, Object argument)170     private Object sendRequest(int command, Object argument) {
171         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
172             throw new RuntimeException("This method will deadlock if called from the main thread.");
173         }
174 
175         MainThreadRequest request = new MainThreadRequest(argument);
176         Message msg = mMainThreadHandler.obtainMessage(command, request);
177         msg.sendToTarget();
178 
179         // Wait for the request to complete
180         synchronized (request) {
181             while (request.result == null) {
182                 try {
183                     request.wait();
184                 } catch (InterruptedException e) {
185                     // Do nothing, go back and wait until the request is complete
186                 }
187             }
188         }
189         return request.result;
190     }
191 
192     /**
193      * Asynchronous ("fire and forget") version of sendRequest():
194      * Posts the specified command to be executed on the main thread, and
195      * returns immediately.
196      * @see sendRequest
197      */
sendRequestAsync(int command)198     private void sendRequestAsync(int command) {
199         mMainThreadHandler.sendEmptyMessage(command);
200     }
201 
PhoneInterfaceManager(PhoneApp app, Phone phone)202     public PhoneInterfaceManager(PhoneApp app, Phone phone) {
203         mApp = app;
204         mPhone = phone;
205         mMainThreadHandler = new MainThreadHandler();
206         publish();
207     }
208 
publish()209     private void publish() {
210         if (DBG) log("publish: " + this);
211 
212         ServiceManager.addService("phone", this);
213     }
214 
215     //
216     // Implementation of the ITelephony interface.
217     //
218 
dial(String number)219     public void dial(String number) {
220         if (DBG) log("dial: " + number);
221         // No permission check needed here: This is just a wrapper around the
222         // ACTION_DIAL intent, which is available to any app since it puts up
223         // the UI before it does anything.
224 
225         String url = createTelUrl(number);
226         if (url == null) {
227             return;
228         }
229 
230         // PENDING: should we just silently fail if phone is offhook or ringing?
231         Phone.State state = mPhone.getState();
232         if (state != Phone.State.OFFHOOK && state != Phone.State.RINGING) {
233             Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
234             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
235             mApp.startActivity(intent);
236         }
237     }
238 
call(String number)239     public void call(String number) {
240         if (DBG) log("call: " + number);
241 
242         // This is just a wrapper around the ACTION_CALL intent, but we still
243         // need to do a permission check since we're calling startActivity()
244         // from the context of the phone app.
245         enforceCallPermission();
246 
247         String url = createTelUrl(number);
248         if (url == null) {
249             return;
250         }
251 
252         Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
253         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
254         intent.setClassName(mApp, PhoneApp.getCallScreenClassName());
255         mApp.startActivity(intent);
256     }
257 
showCallScreenInternal(boolean specifyInitialDialpadState, boolean initialDialpadState)258     private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
259                                            boolean initialDialpadState) {
260         if (isIdle()) {
261             return false;
262         }
263         // If the phone isn't idle then go to the in-call screen
264         long callingId = Binder.clearCallingIdentity();
265         try {
266             Intent intent;
267             if (specifyInitialDialpadState) {
268                 intent = PhoneApp.createInCallIntent(initialDialpadState);
269             } else {
270                 intent = PhoneApp.createInCallIntent();
271             }
272             mApp.startActivity(intent);
273         } finally {
274             Binder.restoreCallingIdentity(callingId);
275         }
276         return true;
277     }
278 
279     // Show the in-call screen without specifying the initial dialpad state.
showCallScreen()280     public boolean showCallScreen() {
281         return showCallScreenInternal(false, false);
282     }
283 
284     // The variation of showCallScreen() that specifies the initial dialpad state.
285     // (Ideally this would be called showCallScreen() too, just with a different
286     // signature, but AIDL doesn't allow that.)
showCallScreenWithDialpad(boolean showDialpad)287     public boolean showCallScreenWithDialpad(boolean showDialpad) {
288         return showCallScreenInternal(true, showDialpad);
289     }
290 
291     /**
292      * End a call based on call state
293      * @return true is a call was ended
294      */
endCall()295     public boolean endCall() {
296         enforceCallPermission();
297         return (Boolean) sendRequest(CMD_END_CALL, null);
298     }
299 
answerRingingCall()300     public void answerRingingCall() {
301         if (DBG) log("answerRingingCall...");
302         // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
303         // but that can probably wait till the big TelephonyManager API overhaul.
304         // For now, protect this call with the MODIFY_PHONE_STATE permission.
305         enforceModifyPermission();
306         sendRequestAsync(CMD_ANSWER_RINGING_CALL);
307     }
308 
309     /**
310      * Make the actual telephony calls to implement answerRingingCall().
311      * This should only be called from the main thread of the Phone app.
312      * @see answerRingingCall
313      *
314      * TODO: it would be nice to return true if we answered the call, or
315      * false if there wasn't actually a ringing incoming call, or some
316      * other error occurred.  (In other words, pass back the return value
317      * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
318      * But that would require calling this method via sendRequest() rather
319      * than sendRequestAsync(), and right now we don't actually *need* that
320      * return value, so let's just return void for now.
321      */
answerRingingCallInternal()322     private void answerRingingCallInternal() {
323         final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
324         if (hasRingingCall) {
325             final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
326             final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
327             if (hasActiveCall && hasHoldingCall) {
328                 // Both lines are in use!
329                 // TODO: provide a flag to let the caller specify what
330                 // policy to use if both lines are in use.  (The current
331                 // behavior is hardwired to "answer incoming, end ongoing",
332                 // which is how the CALL button is specced to behave.)
333                 PhoneUtils.answerAndEndActive(mPhone);
334                 return;
335             } else {
336                 // answerCall() will automatically hold the current active
337                 // call, if there is one.
338                 PhoneUtils.answerCall(mPhone);
339                 return;
340             }
341         } else {
342             // No call was ringing.
343             return;
344         }
345     }
346 
silenceRinger()347     public void silenceRinger() {
348         if (DBG) log("silenceRinger...");
349         // TODO: find a more appropriate permission to check here.
350         // (That can probably wait till the big TelephonyManager API overhaul.
351         // For now, protect this call with the MODIFY_PHONE_STATE permission.)
352         enforceModifyPermission();
353         sendRequestAsync(CMD_SILENCE_RINGER);
354     }
355 
356     /**
357      * Internal implemenation of silenceRinger().
358      * This should only be called from the main thread of the Phone app.
359      * @see silenceRinger
360      */
silenceRingerInternal()361     private void silenceRingerInternal() {
362         if ((mPhone.getState() == Phone.State.RINGING)
363             && mApp.notifier.isRinging()) {
364             // Ringer is actually playing, so silence it.
365             if (DBG) log("silenceRingerInternal: silencing...");
366             PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE);
367             mApp.notifier.silenceRinger();
368         }
369     }
370 
isOffhook()371     public boolean isOffhook() {
372         return (mPhone.getState() == Phone.State.OFFHOOK);
373     }
374 
isRinging()375     public boolean isRinging() {
376         return (mPhone.getState() == Phone.State.RINGING);
377     }
378 
isIdle()379     public boolean isIdle() {
380         return (mPhone.getState() == Phone.State.IDLE);
381     }
382 
isSimPinEnabled()383     public boolean isSimPinEnabled() {
384         enforceReadPermission();
385         return (PhoneApp.getInstance().isSimPinEnabled());
386     }
387 
supplyPin(String pin)388     public boolean supplyPin(String pin) {
389         enforceModifyPermission();
390         final CheckSimPin checkSimPin = new CheckSimPin(mPhone.getIccCard());
391         checkSimPin.start();
392         return checkSimPin.checkPin(pin);
393     }
394 
395     /**
396      * Helper thread to turn async call to {@link SimCard#supplyPin} into
397      * a synchronous one.
398      */
399     private static class CheckSimPin extends Thread {
400 
401         private final IccCard mSimCard;
402 
403         private boolean mDone = false;
404         private boolean mResult = false;
405 
406         // For replies from SimCard interface
407         private Handler mHandler;
408 
409         // For async handler to identify request type
410         private static final int SUPPLY_PIN_COMPLETE = 100;
411 
CheckSimPin(IccCard simCard)412         public CheckSimPin(IccCard simCard) {
413             mSimCard = simCard;
414         }
415 
416         @Override
run()417         public void run() {
418             Looper.prepare();
419             synchronized (CheckSimPin.this) {
420                 mHandler = new Handler() {
421                     @Override
422                     public void handleMessage(Message msg) {
423                         AsyncResult ar = (AsyncResult) msg.obj;
424                         switch (msg.what) {
425                             case SUPPLY_PIN_COMPLETE:
426                                 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
427                                 synchronized (CheckSimPin.this) {
428                                     mResult = (ar.exception == null);
429                                     mDone = true;
430                                     CheckSimPin.this.notifyAll();
431                                 }
432                                 break;
433                         }
434                     }
435                 };
436                 CheckSimPin.this.notifyAll();
437             }
438             Looper.loop();
439         }
440 
checkPin(String pin)441         synchronized boolean checkPin(String pin) {
442 
443             while (mHandler == null) {
444                 try {
445                     wait();
446                 } catch (InterruptedException e) {
447                     Thread.currentThread().interrupt();
448                 }
449             }
450             Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
451 
452             mSimCard.supplyPin(pin, callback);
453 
454             while (!mDone) {
455                 try {
456                     Log.d(LOG_TAG, "wait for done");
457                     wait();
458                 } catch (InterruptedException e) {
459                     // Restore the interrupted status
460                     Thread.currentThread().interrupt();
461                 }
462             }
463             Log.d(LOG_TAG, "done");
464             return mResult;
465         }
466     }
467 
updateServiceLocation()468     public void updateServiceLocation() {
469         // No permission check needed here: this call is harmless, and it's
470         // needed for the ServiceState.requestStateUpdate() call (which is
471         // already intentionally exposed to 3rd parties.)
472         mPhone.updateServiceLocation();
473     }
474 
isRadioOn()475     public boolean isRadioOn() {
476         return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
477     }
478 
toggleRadioOnOff()479     public void toggleRadioOnOff() {
480         enforceModifyPermission();
481         mPhone.setRadioPower(!isRadioOn());
482     }
setRadio(boolean turnOn)483     public boolean setRadio(boolean turnOn) {
484         enforceModifyPermission();
485         if ((mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF) != turnOn) {
486             toggleRadioOnOff();
487         }
488         return true;
489     }
490 
enableDataConnectivity()491     public boolean enableDataConnectivity() {
492         enforceModifyPermission();
493         return mPhone.enableDataConnectivity();
494     }
495 
enableApnType(String type)496     public int enableApnType(String type) {
497         enforceModifyPermission();
498         return mPhone.enableApnType(type);
499     }
500 
disableApnType(String type)501     public int disableApnType(String type) {
502         enforceModifyPermission();
503         return mPhone.disableApnType(type);
504     }
505 
disableDataConnectivity()506     public boolean disableDataConnectivity() {
507         enforceModifyPermission();
508         return mPhone.disableDataConnectivity();
509     }
510 
isDataConnectivityPossible()511     public boolean isDataConnectivityPossible() {
512         return mPhone.isDataConnectivityPossible();
513     }
514 
handlePinMmi(String dialString)515     public boolean handlePinMmi(String dialString) {
516         enforceModifyPermission();
517         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
518     }
519 
cancelMissedCallsNotification()520     public void cancelMissedCallsNotification() {
521         enforceModifyPermission();
522         NotificationMgr.getDefault().cancelMissedCallNotification();
523     }
524 
getCallState()525     public int getCallState() {
526         return DefaultPhoneNotifier.convertCallState(mPhone.getState());
527     }
528 
getDataState()529     public int getDataState() {
530         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
531     }
532 
getDataActivity()533     public int getDataActivity() {
534         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
535     }
536 
getCellLocation()537     public Bundle getCellLocation() {
538         try {
539             mApp.enforceCallingOrSelfPermission(
540                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
541         } catch (SecurityException e) {
542             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
543             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
544             // is the weaker precondition
545             mApp.enforceCallingOrSelfPermission(
546                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
547         }
548         Bundle data = new Bundle();
549         mPhone.getCellLocation().fillInNotifierBundle(data);
550         return data;
551     }
552 
enableLocationUpdates()553     public void enableLocationUpdates() {
554         mApp.enforceCallingOrSelfPermission(
555                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
556         mPhone.enableLocationUpdates();
557     }
558 
disableLocationUpdates()559     public void disableLocationUpdates() {
560         mApp.enforceCallingOrSelfPermission(
561                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
562         mPhone.disableLocationUpdates();
563     }
564 
565     @SuppressWarnings("unchecked")
getNeighboringCellInfo()566     public List<NeighboringCellInfo> getNeighboringCellInfo() {
567         try {
568             mApp.enforceCallingOrSelfPermission(
569                     android.Manifest.permission.ACCESS_FINE_LOCATION, null);
570         } catch (SecurityException e) {
571             // If we have ACCESS_FINE_LOCATION permission, skip the check
572             // for ACCESS_COARSE_LOCATION
573             // A failure should throw the SecurityException from
574             // ACCESS_COARSE_LOCATION since this is the weaker precondition
575             mApp.enforceCallingOrSelfPermission(
576                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
577         }
578 
579         ArrayList<NeighboringCellInfo> cells = null;
580 
581         try {
582             cells = (ArrayList<NeighboringCellInfo>) sendRequest(
583                     CMD_HANDLE_NEIGHBORING_CELL, null);
584         } catch (RuntimeException e) {
585             Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
586         }
587 
588         return (List <NeighboringCellInfo>) cells;
589     }
590 
591 
592     //
593     // Internal helper methods.
594     //
595 
596     /**
597      * Make sure the caller has the READ_PHONE_STATE permission.
598      *
599      * @throws SecurityException if the caller does not have the required permission
600      */
enforceReadPermission()601     private void enforceReadPermission() {
602         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
603     }
604 
605     /**
606      * Make sure the caller has the MODIFY_PHONE_STATE permission.
607      *
608      * @throws SecurityException if the caller does not have the required permission
609      */
enforceModifyPermission()610     private void enforceModifyPermission() {
611         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
612     }
613 
614     /**
615      * Make sure the caller has the CALL_PHONE permission.
616      *
617      * @throws SecurityException if the caller does not have the required permission
618      */
enforceCallPermission()619     private void enforceCallPermission() {
620         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
621     }
622 
623 
createTelUrl(String number)624     private String createTelUrl(String number) {
625         if (TextUtils.isEmpty(number)) {
626             return null;
627         }
628 
629         StringBuilder buf = new StringBuilder("tel:");
630         buf.append(number);
631         return buf.toString();
632     }
633 
log(String msg)634     private void log(String msg) {
635         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
636     }
637 
getActivePhoneType()638     public int getActivePhoneType() {
639         return mPhone.getPhoneType();
640     }
641 
642     /**
643      * Returns the CDMA ERI icon index to display
644      */
getCdmaEriIconIndex()645     public int getCdmaEriIconIndex() {
646         return mPhone.getCdmaEriIconIndex();
647     }
648 
649     /**
650      * Returns the CDMA ERI icon mode,
651      * 0 - ON
652      * 1 - FLASHING
653      */
getCdmaEriIconMode()654     public int getCdmaEriIconMode() {
655         return mPhone.getCdmaEriIconMode();
656     }
657 
658     /**
659      * Returns the CDMA ERI text,
660      */
getCdmaEriText()661     public String getCdmaEriText() {
662         return mPhone.getCdmaEriText();
663     }
664 
665     /**
666      * Returns true if CDMA provisioning needs to run.
667      */
getCdmaNeedsProvisioning()668     public boolean getCdmaNeedsProvisioning() {
669         if (getActivePhoneType() == Phone.PHONE_TYPE_GSM) {
670             return false;
671         }
672 
673         boolean needsProvisioning = false;
674         String cdmaMin = mPhone.getCdmaMin();
675         try {
676             needsProvisioning = OtaUtils.needsActivation(cdmaMin);
677         } catch (IllegalArgumentException e) {
678             // shouldn't get here unless hardware is misconfigured
679             Log.e(LOG_TAG, "CDMA MIN string " + ((cdmaMin == null) ? "was null" : "was too short"));
680         }
681         return needsProvisioning;
682     }
683 
684     /**
685      * Returns the unread count of voicemails
686      */
getVoiceMessageCount()687     public int getVoiceMessageCount() {
688         return mPhone.getVoiceMessageCount();
689     }
690 
691     /**
692      * Returns the network type
693      */
getNetworkType()694     public int getNetworkType() {
695         int radiotech = mPhone.getServiceState().getRadioTechnology();
696         switch(radiotech) {
697             case ServiceState.RADIO_TECHNOLOGY_GPRS:
698                 return TelephonyManager.NETWORK_TYPE_GPRS;
699             case ServiceState.RADIO_TECHNOLOGY_EDGE:
700                 return TelephonyManager.NETWORK_TYPE_EDGE;
701             case ServiceState.RADIO_TECHNOLOGY_UMTS:
702                 return TelephonyManager.NETWORK_TYPE_UMTS;
703             case ServiceState.RADIO_TECHNOLOGY_HSDPA:
704                 return TelephonyManager.NETWORK_TYPE_HSDPA;
705             case ServiceState.RADIO_TECHNOLOGY_HSUPA:
706                 return TelephonyManager.NETWORK_TYPE_HSUPA;
707             case ServiceState.RADIO_TECHNOLOGY_HSPA:
708                 return TelephonyManager.NETWORK_TYPE_HSPA;
709             case ServiceState.RADIO_TECHNOLOGY_IS95A:
710             case ServiceState.RADIO_TECHNOLOGY_IS95B:
711                 return TelephonyManager.NETWORK_TYPE_CDMA;
712             case ServiceState.RADIO_TECHNOLOGY_1xRTT:
713                 return TelephonyManager.NETWORK_TYPE_1xRTT;
714             case ServiceState.RADIO_TECHNOLOGY_EVDO_0:
715                 return TelephonyManager.NETWORK_TYPE_EVDO_0;
716             case ServiceState.RADIO_TECHNOLOGY_EVDO_A:
717                 return TelephonyManager.NETWORK_TYPE_EVDO_A;
718             default:
719                 return TelephonyManager.NETWORK_TYPE_UNKNOWN;
720         }
721     }
722 
723     /**
724      * @return true if a ICC card is present
725      */
hasIccCard()726     public boolean hasIccCard() {
727         return mPhone.getIccCard().hasIccCard();
728     }
729 }
730