Home | History | Annotate | Download | only in phone
      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.app.ActivityManager;
     20 import android.app.AppOpsManager;
     21 import android.bluetooth.IBluetoothHeadsetPhone;
     22 import android.content.ActivityNotFoundException;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.net.ConnectivityManager;
     26 import android.net.Uri;
     27 import android.os.AsyncResult;
     28 import android.os.Binder;
     29 import android.os.Bundle;
     30 import android.os.Handler;
     31 import android.os.IBinder;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.Process;
     35 import android.os.RemoteException;
     36 import android.os.ServiceManager;
     37 import android.os.UserHandle;
     38 import android.telephony.NeighboringCellInfo;
     39 import android.telephony.CellInfo;
     40 import android.telephony.ServiceState;
     41 import android.text.TextUtils;
     42 import android.util.Log;
     43 
     44 import com.android.internal.telephony.CallManager;
     45 import com.android.internal.telephony.CommandException;
     46 import com.android.internal.telephony.Connection;
     47 import com.android.internal.telephony.DefaultPhoneNotifier;
     48 import com.android.internal.telephony.IccCard;
     49 import com.android.internal.telephony.ITelephony;
     50 import com.android.internal.telephony.ITelephonyListener;
     51 import com.android.internal.telephony.Phone;
     52 import com.android.internal.telephony.PhoneConstants;
     53 import com.android.services.telephony.common.Call;
     54 
     55 import com.android.internal.util.HexDump;
     56 
     57 import java.util.ArrayList;
     58 import java.util.HashMap;
     59 import java.util.Iterator;
     60 import java.util.List;
     61 import java.util.Map;
     62 
     63 /**
     64  * Implementation of the ITelephony interface.
     65  */
     66 public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
     67     private static final String LOG_TAG = "PhoneInterfaceManager";
     68     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
     69     private static final boolean DBG_LOC = false;
     70 
     71     // Message codes used with mMainThreadHandler
     72     private static final int CMD_HANDLE_PIN_MMI = 1;
     73     private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
     74     private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
     75     private static final int CMD_ANSWER_RINGING_CALL = 4;
     76     private static final int CMD_END_CALL = 5;  // not used yet
     77     private static final int CMD_SILENCE_RINGER = 6;
     78 
     79     /** The singleton instance. */
     80     private static PhoneInterfaceManager sInstance;
     81 
     82     PhoneGlobals mApp;
     83     Phone mPhone;
     84     CallManager mCM;
     85     AppOpsManager mAppOps;
     86     MainThreadHandler mMainThreadHandler;
     87     CallHandlerServiceProxy mCallHandlerService;
     88     CallModeler mCallModeler;
     89     DTMFTonePlayer mDtmfTonePlayer;
     90     Handler mDtmfStopHandler = new Handler();
     91     Runnable mDtmfStopRunnable;
     92 
     93     private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
     94     private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
     95             new HashMap<IBinder, TelephonyListenerDeathRecipient>();
     96 
     97     /**
     98      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
     99      * request after sending. The main thread will notify the request when it is complete.
    100      */
    101     private static final class MainThreadRequest {
    102         /** The argument to use for the request */
    103         public Object argument;
    104         /** The result of the request that is run on the main thread */
    105         public Object result;
    106 
    107         public MainThreadRequest(Object argument) {
    108             this.argument = argument;
    109         }
    110     }
    111 
    112     /**
    113      * A handler that processes messages on the main thread in the phone process. Since many
    114      * of the Phone calls are not thread safe this is needed to shuttle the requests from the
    115      * inbound binder threads to the main thread in the phone process.  The Binder thread
    116      * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
    117      * on, which will be notified when the operation completes and will contain the result of the
    118      * request.
    119      *
    120      * <p>If a MainThreadRequest object is provided in the msg.obj field,
    121      * note that request.result must be set to something non-null for the calling thread to
    122      * unblock.
    123      */
    124     private final class MainThreadHandler extends Handler {
    125         @Override
    126         public void handleMessage(Message msg) {
    127             MainThreadRequest request;
    128             Message onCompleted;
    129             AsyncResult ar;
    130 
    131             switch (msg.what) {
    132                 case CMD_HANDLE_PIN_MMI:
    133                     request = (MainThreadRequest) msg.obj;
    134                     request.result = Boolean.valueOf(
    135                             mPhone.handlePinMmi((String) request.argument));
    136                     // Wake up the requesting thread
    137                     synchronized (request) {
    138                         request.notifyAll();
    139                     }
    140                     break;
    141 
    142                 case CMD_HANDLE_NEIGHBORING_CELL:
    143                     request = (MainThreadRequest) msg.obj;
    144                     onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
    145                             request);
    146                     mPhone.getNeighboringCids(onCompleted);
    147                     break;
    148 
    149                 case EVENT_NEIGHBORING_CELL_DONE:
    150                     ar = (AsyncResult) msg.obj;
    151                     request = (MainThreadRequest) ar.userObj;
    152                     if (ar.exception == null && ar.result != null) {
    153                         request.result = ar.result;
    154                     } else {
    155                         // create an empty list to notify the waiting thread
    156                         request.result = new ArrayList<NeighboringCellInfo>();
    157                     }
    158                     // Wake up the requesting thread
    159                     synchronized (request) {
    160                         request.notifyAll();
    161                     }
    162                     break;
    163 
    164                 case CMD_ANSWER_RINGING_CALL:
    165                     answerRingingCallInternal();
    166                     break;
    167 
    168                 case CMD_SILENCE_RINGER:
    169                     silenceRingerInternal();
    170                     break;
    171 
    172                 case CMD_END_CALL:
    173                     request = (MainThreadRequest) msg.obj;
    174                     boolean hungUp = false;
    175                     int phoneType = mPhone.getPhoneType();
    176                     if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    177                         // CDMA: If the user presses the Power button we treat it as
    178                         // ending the complete call session
    179                         hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
    180                     } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    181                         // GSM: End the call as per the Phone state
    182                         hungUp = PhoneUtils.hangup(mCM);
    183                     } else {
    184                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
    185                     }
    186                     if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
    187                     request.result = hungUp;
    188                     // Wake up the requesting thread
    189                     synchronized (request) {
    190                         request.notifyAll();
    191                     }
    192                     break;
    193 
    194                 default:
    195                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
    196                     break;
    197             }
    198         }
    199     }
    200 
    201     /**
    202      * Posts the specified command to be executed on the main thread,
    203      * waits for the request to complete, and returns the result.
    204      * @see #sendRequestAsync
    205      */
    206     private Object sendRequest(int command, Object argument) {
    207         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
    208             throw new RuntimeException("This method will deadlock if called from the main thread.");
    209         }
    210 
    211         MainThreadRequest request = new MainThreadRequest(argument);
    212         Message msg = mMainThreadHandler.obtainMessage(command, request);
    213         msg.sendToTarget();
    214 
    215         // Wait for the request to complete
    216         synchronized (request) {
    217             while (request.result == null) {
    218                 try {
    219                     request.wait();
    220                 } catch (InterruptedException e) {
    221                     // Do nothing, go back and wait until the request is complete
    222                 }
    223             }
    224         }
    225         return request.result;
    226     }
    227 
    228     /**
    229      * Asynchronous ("fire and forget") version of sendRequest():
    230      * Posts the specified command to be executed on the main thread, and
    231      * returns immediately.
    232      * @see #sendRequest
    233      */
    234     private void sendRequestAsync(int command) {
    235         mMainThreadHandler.sendEmptyMessage(command);
    236     }
    237 
    238     /**
    239      * Initialize the singleton PhoneInterfaceManager instance.
    240      * This is only done once, at startup, from PhoneApp.onCreate().
    241      */
    242     /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
    243                 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
    244                 DTMFTonePlayer dtmfTonePlayer) {
    245         synchronized (PhoneInterfaceManager.class) {
    246             if (sInstance == null) {
    247                 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
    248                         dtmfTonePlayer);
    249             } else {
    250                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    251             }
    252             return sInstance;
    253         }
    254     }
    255 
    256     /** Private constructor; @see init() */
    257     private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
    258             CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
    259             DTMFTonePlayer dtmfTonePlayer) {
    260         mApp = app;
    261         mPhone = phone;
    262         mCM = PhoneGlobals.getInstance().mCM;
    263         mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
    264         mMainThreadHandler = new MainThreadHandler();
    265         mCallHandlerService = callHandlerService;
    266         mCallModeler = callModeler;
    267         mCallModeler.addListener(this);
    268         mDtmfTonePlayer = dtmfTonePlayer;
    269         publish();
    270     }
    271 
    272     private void publish() {
    273         if (DBG) log("publish: " + this);
    274 
    275         ServiceManager.addService("phone", this);
    276     }
    277 
    278     //
    279     // Implementation of the ITelephony interface.
    280     //
    281 
    282     public void dial(String number) {
    283         if (DBG) log("dial: " + number);
    284         // No permission check needed here: This is just a wrapper around the
    285         // ACTION_DIAL intent, which is available to any app since it puts up
    286         // the UI before it does anything.
    287 
    288         String url = createTelUrl(number);
    289         if (url == null) {
    290             return;
    291         }
    292 
    293         // PENDING: should we just silently fail if phone is offhook or ringing?
    294         PhoneConstants.State state = mCM.getState();
    295         if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
    296             Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
    297             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    298             mApp.startActivity(intent);
    299         }
    300     }
    301 
    302     public void call(String callingPackage, String number) {
    303         if (DBG) log("call: " + number);
    304 
    305         // This is just a wrapper around the ACTION_CALL intent, but we still
    306         // need to do a permission check since we're calling startActivity()
    307         // from the context of the phone app.
    308         enforceCallPermission();
    309 
    310         if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
    311                 != AppOpsManager.MODE_ALLOWED) {
    312             return;
    313         }
    314 
    315         String url = createTelUrl(number);
    316         if (url == null) {
    317             return;
    318         }
    319 
    320         Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
    321         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    322         mApp.startActivity(intent);
    323     }
    324 
    325     private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
    326                                            boolean showDialpad) {
    327         if (!PhoneGlobals.sVoiceCapable) {
    328             // Never allow the InCallScreen to appear on data-only devices.
    329             return false;
    330         }
    331         if (isIdle()) {
    332             return false;
    333         }
    334         // If the phone isn't idle then go to the in-call screen
    335         long callingId = Binder.clearCallingIdentity();
    336 
    337         mCallHandlerService.bringToForeground(showDialpad);
    338 
    339         Binder.restoreCallingIdentity(callingId);
    340         return true;
    341     }
    342 
    343     // Show the in-call screen without specifying the initial dialpad state.
    344     public boolean showCallScreen() {
    345         return showCallScreenInternal(false, false);
    346     }
    347 
    348     // The variation of showCallScreen() that specifies the initial dialpad state.
    349     // (Ideally this would be called showCallScreen() too, just with a different
    350     // signature, but AIDL doesn't allow that.)
    351     public boolean showCallScreenWithDialpad(boolean showDialpad) {
    352         return showCallScreenInternal(true, showDialpad);
    353     }
    354 
    355     /**
    356      * End a call based on call state
    357      * @return true is a call was ended
    358      */
    359     public boolean endCall() {
    360         enforceCallPermission();
    361         return (Boolean) sendRequest(CMD_END_CALL, null);
    362     }
    363 
    364     public void answerRingingCall() {
    365         if (DBG) log("answerRingingCall...");
    366         // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
    367         // but that can probably wait till the big TelephonyManager API overhaul.
    368         // For now, protect this call with the MODIFY_PHONE_STATE permission.
    369         enforceModifyPermission();
    370         sendRequestAsync(CMD_ANSWER_RINGING_CALL);
    371     }
    372 
    373     /**
    374      * Make the actual telephony calls to implement answerRingingCall().
    375      * This should only be called from the main thread of the Phone app.
    376      * @see #answerRingingCall
    377      *
    378      * TODO: it would be nice to return true if we answered the call, or
    379      * false if there wasn't actually a ringing incoming call, or some
    380      * other error occurred.  (In other words, pass back the return value
    381      * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
    382      * But that would require calling this method via sendRequest() rather
    383      * than sendRequestAsync(), and right now we don't actually *need* that
    384      * return value, so let's just return void for now.
    385      */
    386     private void answerRingingCallInternal() {
    387         final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
    388         if (hasRingingCall) {
    389             final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
    390             final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
    391             if (hasActiveCall && hasHoldingCall) {
    392                 // Both lines are in use!
    393                 // TODO: provide a flag to let the caller specify what
    394                 // policy to use if both lines are in use.  (The current
    395                 // behavior is hardwired to "answer incoming, end ongoing",
    396                 // which is how the CALL button is specced to behave.)
    397                 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
    398                 return;
    399             } else {
    400                 // answerCall() will automatically hold the current active
    401                 // call, if there is one.
    402                 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
    403                 return;
    404             }
    405         } else {
    406             // No call was ringing.
    407             return;
    408         }
    409     }
    410 
    411     public void silenceRinger() {
    412         if (DBG) log("silenceRinger...");
    413         // TODO: find a more appropriate permission to check here.
    414         // (That can probably wait till the big TelephonyManager API overhaul.
    415         // For now, protect this call with the MODIFY_PHONE_STATE permission.)
    416         enforceModifyPermission();
    417         sendRequestAsync(CMD_SILENCE_RINGER);
    418     }
    419 
    420     /**
    421      * Internal implemenation of silenceRinger().
    422      * This should only be called from the main thread of the Phone app.
    423      * @see #silenceRinger
    424      */
    425     private void silenceRingerInternal() {
    426         if ((mCM.getState() == PhoneConstants.State.RINGING)
    427             && mApp.notifier.isRinging()) {
    428             // Ringer is actually playing, so silence it.
    429             if (DBG) log("silenceRingerInternal: silencing...");
    430             mApp.notifier.silenceRinger();
    431         }
    432     }
    433 
    434     public boolean isOffhook() {
    435         return (mCM.getState() == PhoneConstants.State.OFFHOOK);
    436     }
    437 
    438     public boolean isRinging() {
    439         return (mCM.getState() == PhoneConstants.State.RINGING);
    440     }
    441 
    442     public boolean isIdle() {
    443         return (mCM.getState() == PhoneConstants.State.IDLE);
    444     }
    445 
    446     public boolean isSimPinEnabled() {
    447         enforceReadPermission();
    448         return (PhoneGlobals.getInstance().isSimPinEnabled());
    449     }
    450 
    451     public boolean supplyPin(String pin) {
    452         int [] resultArray = supplyPinReportResult(pin);
    453         return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
    454     }
    455 
    456     public boolean supplyPuk(String puk, String pin) {
    457         int [] resultArray = supplyPukReportResult(puk, pin);
    458         return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
    459     }
    460 
    461     /** {@hide} */
    462     public int[] supplyPinReportResult(String pin) {
    463         enforceModifyPermission();
    464         final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
    465         checkSimPin.start();
    466         return checkSimPin.unlockSim(null, pin);
    467     }
    468 
    469     /** {@hide} */
    470     public int[] supplyPukReportResult(String puk, String pin) {
    471         enforceModifyPermission();
    472         final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
    473         checkSimPuk.start();
    474         return checkSimPuk.unlockSim(puk, pin);
    475     }
    476 
    477     /**
    478      * Helper thread to turn async call to SimCard#supplyPin into
    479      * a synchronous one.
    480      */
    481     private static class UnlockSim extends Thread {
    482 
    483         private final IccCard mSimCard;
    484 
    485         private boolean mDone = false;
    486         private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
    487         private int mRetryCount = -1;
    488 
    489         // For replies from SimCard interface
    490         private Handler mHandler;
    491 
    492         // For async handler to identify request type
    493         private static final int SUPPLY_PIN_COMPLETE = 100;
    494 
    495         public UnlockSim(IccCard simCard) {
    496             mSimCard = simCard;
    497         }
    498 
    499         @Override
    500         public void run() {
    501             Looper.prepare();
    502             synchronized (UnlockSim.this) {
    503                 mHandler = new Handler() {
    504                     @Override
    505                     public void handleMessage(Message msg) {
    506                         AsyncResult ar = (AsyncResult) msg.obj;
    507                         switch (msg.what) {
    508                             case SUPPLY_PIN_COMPLETE:
    509                                 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
    510                                 synchronized (UnlockSim.this) {
    511                                     mRetryCount = msg.arg1;
    512                                     if (ar.exception != null) {
    513                                         if (ar.exception instanceof CommandException &&
    514                                                 ((CommandException)(ar.exception)).getCommandError()
    515                                                 == CommandException.Error.PASSWORD_INCORRECT) {
    516                                             mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
    517                                         } else {
    518                                             mResult = PhoneConstants.PIN_GENERAL_FAILURE;
    519                                         }
    520                                     } else {
    521                                         mResult = PhoneConstants.PIN_RESULT_SUCCESS;
    522                                     }
    523                                     mDone = true;
    524                                     UnlockSim.this.notifyAll();
    525                                 }
    526                                 break;
    527                         }
    528                     }
    529                 };
    530                 UnlockSim.this.notifyAll();
    531             }
    532             Looper.loop();
    533         }
    534 
    535         /*
    536          * Use PIN or PUK to unlock SIM card
    537          *
    538          * If PUK is null, unlock SIM card with PIN
    539          *
    540          * If PUK is not null, unlock SIM card with PUK and set PIN code
    541          */
    542         synchronized int[] unlockSim(String puk, String pin) {
    543 
    544             while (mHandler == null) {
    545                 try {
    546                     wait();
    547                 } catch (InterruptedException e) {
    548                     Thread.currentThread().interrupt();
    549                 }
    550             }
    551             Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
    552 
    553             if (puk == null) {
    554                 mSimCard.supplyPin(pin, callback);
    555             } else {
    556                 mSimCard.supplyPuk(puk, pin, callback);
    557             }
    558 
    559             while (!mDone) {
    560                 try {
    561                     Log.d(LOG_TAG, "wait for done");
    562                     wait();
    563                 } catch (InterruptedException e) {
    564                     // Restore the interrupted status
    565                     Thread.currentThread().interrupt();
    566                 }
    567             }
    568             Log.d(LOG_TAG, "done");
    569             int[] resultArray = new int[2];
    570             resultArray[0] = mResult;
    571             resultArray[1] = mRetryCount;
    572             return resultArray;
    573         }
    574     }
    575 
    576     public void updateServiceLocation() {
    577         // No permission check needed here: this call is harmless, and it's
    578         // needed for the ServiceState.requestStateUpdate() call (which is
    579         // already intentionally exposed to 3rd parties.)
    580         mPhone.updateServiceLocation();
    581     }
    582 
    583     public boolean isRadioOn() {
    584         return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
    585     }
    586 
    587     public void toggleRadioOnOff() {
    588         enforceModifyPermission();
    589         mPhone.setRadioPower(!isRadioOn());
    590     }
    591     public boolean setRadio(boolean turnOn) {
    592         enforceModifyPermission();
    593         if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
    594             toggleRadioOnOff();
    595         }
    596         return true;
    597     }
    598     public boolean setRadioPower(boolean turnOn) {
    599         enforceModifyPermission();
    600         mPhone.setRadioPower(turnOn);
    601         return true;
    602     }
    603 
    604     public boolean enableDataConnectivity() {
    605         enforceModifyPermission();
    606         ConnectivityManager cm =
    607                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
    608         cm.setMobileDataEnabled(true);
    609         return true;
    610     }
    611 
    612     public int enableApnType(String type) {
    613         enforceModifyPermission();
    614         return mPhone.enableApnType(type);
    615     }
    616 
    617     public int disableApnType(String type) {
    618         enforceModifyPermission();
    619         return mPhone.disableApnType(type);
    620     }
    621 
    622     public boolean disableDataConnectivity() {
    623         enforceModifyPermission();
    624         ConnectivityManager cm =
    625                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
    626         cm.setMobileDataEnabled(false);
    627         return true;
    628     }
    629 
    630     public boolean isDataConnectivityPossible() {
    631         return mPhone.isDataConnectivityPossible();
    632     }
    633 
    634     public boolean handlePinMmi(String dialString) {
    635         enforceModifyPermission();
    636         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
    637     }
    638 
    639     public void cancelMissedCallsNotification() {
    640         enforceModifyPermission();
    641         mApp.notificationMgr.cancelMissedCallNotification();
    642     }
    643 
    644     public int getCallState() {
    645         return DefaultPhoneNotifier.convertCallState(mCM.getState());
    646     }
    647 
    648     public int getDataState() {
    649         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
    650     }
    651 
    652     public int getDataActivity() {
    653         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
    654     }
    655 
    656     @Override
    657     public Bundle getCellLocation() {
    658         try {
    659             mApp.enforceCallingOrSelfPermission(
    660                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    661         } catch (SecurityException e) {
    662             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
    663             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
    664             // is the weaker precondition
    665             mApp.enforceCallingOrSelfPermission(
    666                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    667         }
    668 
    669         if (checkIfCallerIsSelfOrForegoundUser()) {
    670             if (DBG_LOC) log("getCellLocation: is active user");
    671             Bundle data = new Bundle();
    672             mPhone.getCellLocation().fillInNotifierBundle(data);
    673             return data;
    674         } else {
    675             if (DBG_LOC) log("getCellLocation: suppress non-active user");
    676             return null;
    677         }
    678     }
    679 
    680     @Override
    681     public void enableLocationUpdates() {
    682         mApp.enforceCallingOrSelfPermission(
    683                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
    684         mPhone.enableLocationUpdates();
    685     }
    686 
    687     @Override
    688     public void disableLocationUpdates() {
    689         mApp.enforceCallingOrSelfPermission(
    690                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
    691         mPhone.disableLocationUpdates();
    692     }
    693 
    694     @Override
    695     @SuppressWarnings("unchecked")
    696     public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
    697         try {
    698             mApp.enforceCallingOrSelfPermission(
    699                     android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    700         } catch (SecurityException e) {
    701             // If we have ACCESS_FINE_LOCATION permission, skip the check
    702             // for ACCESS_COARSE_LOCATION
    703             // A failure should throw the SecurityException from
    704             // ACCESS_COARSE_LOCATION since this is the weaker precondition
    705             mApp.enforceCallingOrSelfPermission(
    706                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    707         }
    708 
    709         if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
    710                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    711             return null;
    712         }
    713         if (checkIfCallerIsSelfOrForegoundUser()) {
    714             if (DBG_LOC) log("getNeighboringCellInfo: is active user");
    715 
    716             ArrayList<NeighboringCellInfo> cells = null;
    717 
    718             try {
    719                 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
    720                         CMD_HANDLE_NEIGHBORING_CELL, null);
    721             } catch (RuntimeException e) {
    722                 Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
    723             }
    724             return cells;
    725         } else {
    726             if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
    727             return null;
    728         }
    729     }
    730 
    731 
    732     @Override
    733     public List<CellInfo> getAllCellInfo() {
    734         try {
    735             mApp.enforceCallingOrSelfPermission(
    736                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    737         } catch (SecurityException e) {
    738             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
    739             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
    740             // is the weaker precondition
    741             mApp.enforceCallingOrSelfPermission(
    742                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    743         }
    744 
    745         if (checkIfCallerIsSelfOrForegoundUser()) {
    746             if (DBG_LOC) log("getAllCellInfo: is active user");
    747             return mPhone.getAllCellInfo();
    748         } else {
    749             if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
    750             return null;
    751         }
    752     }
    753 
    754     public void setCellInfoListRate(int rateInMillis) {
    755         mPhone.setCellInfoListRate(rateInMillis);
    756     }
    757 
    758     //
    759     // Internal helper methods.
    760     //
    761 
    762     private boolean checkIfCallerIsSelfOrForegoundUser() {
    763         boolean ok;
    764 
    765         boolean self = Binder.getCallingUid() == Process.myUid();
    766         if (!self) {
    767             // Get the caller's user id then clear the calling identity
    768             // which will be restored in the finally clause.
    769             int callingUser = UserHandle.getCallingUserId();
    770             long ident = Binder.clearCallingIdentity();
    771 
    772             try {
    773                 // With calling identity cleared the current user is the foreground user.
    774                 int foregroundUser = ActivityManager.getCurrentUser();
    775                 ok = (foregroundUser == callingUser);
    776                 if (DBG_LOC) {
    777                     log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
    778                             + " callingUser=" + callingUser + " ok=" + ok);
    779                 }
    780             } catch (Exception ex) {
    781                 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
    782                 ok = false;
    783             } finally {
    784                 Binder.restoreCallingIdentity(ident);
    785             }
    786         } else {
    787             if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
    788             ok = true;
    789         }
    790         if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
    791         return ok;
    792     }
    793 
    794     /**
    795      * Make sure the caller has the READ_PHONE_STATE permission.
    796      *
    797      * @throws SecurityException if the caller does not have the required permission
    798      */
    799     private void enforceReadPermission() {
    800         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
    801     }
    802 
    803     /**
    804      * Make sure the caller has the MODIFY_PHONE_STATE permission.
    805      *
    806      * @throws SecurityException if the caller does not have the required permission
    807      */
    808     private void enforceModifyPermission() {
    809         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
    810     }
    811 
    812     /**
    813      * Make sure the caller has the CALL_PHONE permission.
    814      *
    815      * @throws SecurityException if the caller does not have the required permission
    816      */
    817     private void enforceCallPermission() {
    818         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
    819     }
    820 
    821     /**
    822      * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
    823      *
    824      * @throws SecurityException if the caller does not have the required permission
    825      */
    826     private void enforcePrivilegedPhoneStatePermission() {
    827         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
    828                 null);
    829     }
    830 
    831     private String createTelUrl(String number) {
    832         if (TextUtils.isEmpty(number)) {
    833             return null;
    834         }
    835 
    836         StringBuilder buf = new StringBuilder("tel:");
    837         buf.append(number);
    838         return buf.toString();
    839     }
    840 
    841     private void log(String msg) {
    842         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
    843     }
    844 
    845     private void loge(String msg) {
    846         Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
    847     }
    848 
    849     public int getActivePhoneType() {
    850         return mPhone.getPhoneType();
    851     }
    852 
    853     /**
    854      * Returns the CDMA ERI icon index to display
    855      */
    856     public int getCdmaEriIconIndex() {
    857         return mPhone.getCdmaEriIconIndex();
    858     }
    859 
    860     /**
    861      * Returns the CDMA ERI icon mode,
    862      * 0 - ON
    863      * 1 - FLASHING
    864      */
    865     public int getCdmaEriIconMode() {
    866         return mPhone.getCdmaEriIconMode();
    867     }
    868 
    869     /**
    870      * Returns the CDMA ERI text,
    871      */
    872     public String getCdmaEriText() {
    873         return mPhone.getCdmaEriText();
    874     }
    875 
    876     /**
    877      * Returns true if CDMA provisioning needs to run.
    878      */
    879     public boolean needsOtaServiceProvisioning() {
    880         return mPhone.needsOtaServiceProvisioning();
    881     }
    882 
    883     /**
    884      * Returns the unread count of voicemails
    885      */
    886     public int getVoiceMessageCount() {
    887         return mPhone.getVoiceMessageCount();
    888     }
    889 
    890     /**
    891      * Returns the data network type
    892      *
    893      * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
    894      */
    895     @Override
    896     public int getNetworkType() {
    897         return mPhone.getServiceState().getDataNetworkType();
    898     }
    899 
    900     /**
    901      * Returns the data network type
    902      */
    903     @Override
    904     public int getDataNetworkType() {
    905         return mPhone.getServiceState().getDataNetworkType();
    906     }
    907 
    908     /**
    909      * Returns the data network type
    910      */
    911     @Override
    912     public int getVoiceNetworkType() {
    913         return mPhone.getServiceState().getVoiceNetworkType();
    914     }
    915 
    916     /**
    917      * @return true if a ICC card is present
    918      */
    919     public boolean hasIccCard() {
    920         return mPhone.getIccCard().hasIccCard();
    921     }
    922 
    923     /**
    924      * Return if the current radio is LTE on CDMA. This
    925      * is a tri-state return value as for a period of time
    926      * the mode may be unknown.
    927      *
    928      * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
    929      * or {@link PHone#LTE_ON_CDMA_TRUE}
    930      */
    931     public int getLteOnCdmaMode() {
    932         return mPhone.getLteOnCdmaMode();
    933     }
    934 
    935     @Override
    936     public void toggleHold() {
    937         enforceModifyPermission();
    938 
    939         try {
    940             PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
    941         } catch (Exception e) {
    942             Log.e(LOG_TAG, "Error during toggleHold().", e);
    943         }
    944     }
    945 
    946     @Override
    947     public void merge() {
    948         enforceModifyPermission();
    949 
    950         try {
    951             if (PhoneUtils.okToMergeCalls(mCM)) {
    952                 PhoneUtils.mergeCalls(mCM);
    953             }
    954         } catch (Exception e) {
    955             Log.e(LOG_TAG, "Error during merge().", e);
    956         }
    957     }
    958 
    959     @Override
    960     public void swap() {
    961         enforceModifyPermission();
    962 
    963         try {
    964             PhoneUtils.swap();
    965         } catch (Exception e) {
    966             Log.e(LOG_TAG, "Error during swap().", e);
    967         }
    968     }
    969 
    970     @Override
    971     public void mute(boolean onOff) {
    972         enforceModifyPermission();
    973 
    974         try {
    975             PhoneUtils.setMute(onOff);
    976         } catch (Exception e) {
    977             Log.e(LOG_TAG, "Error during mute().", e);
    978         }
    979     }
    980 
    981     @Override
    982     public void playDtmfTone(char digit, boolean timedShortTone) {
    983         enforceModifyPermission();
    984 
    985         synchronized (mDtmfStopHandler) {
    986             try {
    987                 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
    988             } catch (Exception e) {
    989                 Log.e(LOG_TAG, "Error playing DTMF tone.", e);
    990             }
    991 
    992             if (mDtmfStopRunnable != null) {
    993                 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
    994             }
    995             mDtmfStopRunnable = new Runnable() {
    996                 @Override
    997                 public void run() {
    998                     synchronized (mDtmfStopHandler) {
    999                         if (mDtmfStopRunnable == this) {
   1000                             mDtmfTonePlayer.stopDtmfTone();
   1001                             mDtmfStopRunnable = null;
   1002                         }
   1003                     }
   1004                 }
   1005             };
   1006             mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
   1007         }
   1008     }
   1009 
   1010     @Override
   1011     public void stopDtmfTone() {
   1012         enforceModifyPermission();
   1013 
   1014         synchronized (mDtmfStopHandler) {
   1015             try {
   1016                 mDtmfTonePlayer.stopDtmfTone();
   1017             } catch (Exception e) {
   1018                 Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
   1019             }
   1020 
   1021             if (mDtmfStopRunnable != null) {
   1022                 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
   1023                 mDtmfStopRunnable = null;
   1024             }
   1025         }
   1026     }
   1027 
   1028     @Override
   1029     public void addListener(ITelephonyListener listener) {
   1030         enforcePrivilegedPhoneStatePermission();
   1031 
   1032         if (listener == null) {
   1033             throw new IllegalArgumentException("Listener must not be null.");
   1034         }
   1035 
   1036         synchronized (mListeners) {
   1037             IBinder listenerBinder = listener.asBinder();
   1038             for (ITelephonyListener l : mListeners) {
   1039                 if (l.asBinder().equals(listenerBinder)) {
   1040                     Log.w(LOG_TAG, "Listener already registered. Ignoring.");
   1041                     return;
   1042                 }
   1043             }
   1044             mListeners.add(listener);
   1045             mDeathRecipients.put(listener.asBinder(),
   1046                     new TelephonyListenerDeathRecipient(listener.asBinder()));
   1047 
   1048             // update the new listener so they get the full call state immediately
   1049             for (Call call : mCallModeler.getFullList()) {
   1050                 try {
   1051                     notifyListenerOfCallLocked(call, listener);
   1052                 } catch (RemoteException e) {
   1053                     Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
   1054                     removeListenerInternal(listener);
   1055                 }
   1056             }
   1057         }
   1058     }
   1059 
   1060     @Override
   1061     public void removeListener(ITelephonyListener listener) {
   1062         enforcePrivilegedPhoneStatePermission();
   1063 
   1064         if (listener == null) {
   1065             throw new IllegalArgumentException("Listener must not be null.");
   1066         }
   1067 
   1068         removeListenerInternal(listener);
   1069     }
   1070 
   1071     private void removeListenerInternal(ITelephonyListener listener) {
   1072         IBinder listenerBinder = listener.asBinder();
   1073 
   1074         synchronized (mListeners) {
   1075             for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
   1076                 ITelephonyListener nextListener = it.next();
   1077                 if (nextListener.asBinder().equals(listenerBinder)) {
   1078                     TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
   1079                     if (dr != null) {
   1080                         dr.unlinkDeathRecipient();
   1081                     }
   1082                     it.remove();
   1083                 }
   1084             }
   1085         }
   1086     }
   1087 
   1088     /** CallModeler.Listener implementation **/
   1089 
   1090     @Override
   1091     public void onDisconnect(Call call) {
   1092         notifyListenersOfCall(call);
   1093     }
   1094 
   1095     @Override
   1096     public void onIncoming(Call call) {
   1097         notifyListenersOfCall(call);
   1098     }
   1099 
   1100     @Override
   1101     public void onUpdate(List<Call> calls) {
   1102         for (Call call : calls) {
   1103             notifyListenersOfCall(call);
   1104         }
   1105     }
   1106 
   1107     @Override
   1108     public void onPostDialAction(
   1109             Connection.PostDialState state, int callId, String remainingChars, char c) { }
   1110 
   1111     private void notifyListenersOfCall(Call call) {
   1112         synchronized (mListeners) {
   1113             for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
   1114                 ITelephonyListener listener = it.next();
   1115                 try {
   1116                     notifyListenerOfCallLocked(call, listener);
   1117                 } catch (RemoteException e) {
   1118                     TelephonyListenerDeathRecipient deathRecipient =
   1119                             mDeathRecipients.get(listener.asBinder());
   1120                     if (deathRecipient != null) {
   1121                         deathRecipient.unlinkDeathRecipient();
   1122                     }
   1123                     it.remove();
   1124                 }
   1125             }
   1126         }
   1127     }
   1128 
   1129     private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
   1130             throws RemoteException {
   1131         if (Binder.isProxy(listener)) {
   1132             listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
   1133         } else {
   1134             mMainThreadHandler.post(new Runnable() {
   1135 
   1136                 @Override
   1137                 public void run() {
   1138                     try {
   1139                         listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
   1140                     } catch (RemoteException e) {
   1141                         Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
   1142                     }
   1143                 }
   1144             });
   1145         }
   1146 
   1147     }
   1148 
   1149     private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
   1150         private final IBinder mBinder;
   1151 
   1152         public TelephonyListenerDeathRecipient(IBinder listener) {
   1153             mBinder = listener;
   1154             try {
   1155                 mBinder.linkToDeath(this, 0);
   1156             } catch (RemoteException e) {
   1157                 unlinkDeathRecipient();
   1158             }
   1159         }
   1160 
   1161         @Override
   1162         public void binderDied() {
   1163             synchronized (mListeners) {
   1164                 if (mListeners.contains(mBinder)) {
   1165                     mListeners.remove(mBinder);
   1166                     Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
   1167                 } else {
   1168                     Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
   1169                             "is not registered.");
   1170                 }
   1171             }
   1172         }
   1173 
   1174         public void unlinkDeathRecipient() {
   1175             mBinder.unlinkToDeath(this, 0);
   1176         }
   1177     }
   1178 }
   1179