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.content.ActivityNotFoundException;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.net.ConnectivityManager;
     25 import android.net.Uri;
     26 import android.os.AsyncResult;
     27 import android.os.Binder;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.os.Looper;
     31 import android.os.Message;
     32 import android.os.Process;
     33 import android.os.ServiceManager;
     34 import android.os.UserHandle;
     35 import android.telephony.NeighboringCellInfo;
     36 import android.telephony.CellInfo;
     37 import android.telephony.ServiceState;
     38 import android.text.TextUtils;
     39 import android.util.Log;
     40 
     41 import com.android.internal.telephony.DefaultPhoneNotifier;
     42 import com.android.internal.telephony.IccCard;
     43 import com.android.internal.telephony.ITelephony;
     44 import com.android.internal.telephony.Phone;
     45 import com.android.internal.telephony.CallManager;
     46 import com.android.internal.telephony.PhoneConstants;
     47 
     48 import java.util.List;
     49 import java.util.ArrayList;
     50 
     51 /**
     52  * Implementation of the ITelephony interface.
     53  */
     54 public class PhoneInterfaceManager extends ITelephony.Stub {
     55     private static final String LOG_TAG = "PhoneInterfaceManager";
     56     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
     57     private static final boolean DBG_LOC = false;
     58 
     59     // Message codes used with mMainThreadHandler
     60     private static final int CMD_HANDLE_PIN_MMI = 1;
     61     private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
     62     private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
     63     private static final int CMD_ANSWER_RINGING_CALL = 4;
     64     private static final int CMD_END_CALL = 5;  // not used yet
     65     private static final int CMD_SILENCE_RINGER = 6;
     66 
     67     /** The singleton instance. */
     68     private static PhoneInterfaceManager sInstance;
     69 
     70     PhoneGlobals mApp;
     71     Phone mPhone;
     72     CallManager mCM;
     73     AppOpsManager mAppOps;
     74     MainThreadHandler mMainThreadHandler;
     75 
     76     /**
     77      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
     78      * request after sending. The main thread will notify the request when it is complete.
     79      */
     80     private static final class MainThreadRequest {
     81         /** The argument to use for the request */
     82         public Object argument;
     83         /** The result of the request that is run on the main thread */
     84         public Object result;
     85 
     86         public MainThreadRequest(Object argument) {
     87             this.argument = argument;
     88         }
     89     }
     90 
     91     /**
     92      * A handler that processes messages on the main thread in the phone process. Since many
     93      * of the Phone calls are not thread safe this is needed to shuttle the requests from the
     94      * inbound binder threads to the main thread in the phone process.  The Binder thread
     95      * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
     96      * on, which will be notified when the operation completes and will contain the result of the
     97      * request.
     98      *
     99      * <p>If a MainThreadRequest object is provided in the msg.obj field,
    100      * note that request.result must be set to something non-null for the calling thread to
    101      * unblock.
    102      */
    103     private final class MainThreadHandler extends Handler {
    104         @Override
    105         public void handleMessage(Message msg) {
    106             MainThreadRequest request;
    107             Message onCompleted;
    108             AsyncResult ar;
    109 
    110             switch (msg.what) {
    111                 case CMD_HANDLE_PIN_MMI:
    112                     request = (MainThreadRequest) msg.obj;
    113                     request.result = Boolean.valueOf(
    114                             mPhone.handlePinMmi((String) request.argument));
    115                     // Wake up the requesting thread
    116                     synchronized (request) {
    117                         request.notifyAll();
    118                     }
    119                     break;
    120 
    121                 case CMD_HANDLE_NEIGHBORING_CELL:
    122                     request = (MainThreadRequest) msg.obj;
    123                     onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
    124                             request);
    125                     mPhone.getNeighboringCids(onCompleted);
    126                     break;
    127 
    128                 case EVENT_NEIGHBORING_CELL_DONE:
    129                     ar = (AsyncResult) msg.obj;
    130                     request = (MainThreadRequest) ar.userObj;
    131                     if (ar.exception == null && ar.result != null) {
    132                         request.result = ar.result;
    133                     } else {
    134                         // create an empty list to notify the waiting thread
    135                         request.result = new ArrayList<NeighboringCellInfo>();
    136                     }
    137                     // Wake up the requesting thread
    138                     synchronized (request) {
    139                         request.notifyAll();
    140                     }
    141                     break;
    142 
    143                 case CMD_ANSWER_RINGING_CALL:
    144                     answerRingingCallInternal();
    145                     break;
    146 
    147                 case CMD_SILENCE_RINGER:
    148                     silenceRingerInternal();
    149                     break;
    150 
    151                 case CMD_END_CALL:
    152                     request = (MainThreadRequest) msg.obj;
    153                     boolean hungUp = false;
    154                     int phoneType = mPhone.getPhoneType();
    155                     if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
    156                         // CDMA: If the user presses the Power button we treat it as
    157                         // ending the complete call session
    158                         hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
    159                     } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
    160                         // GSM: End the call as per the Phone state
    161                         hungUp = PhoneUtils.hangup(mCM);
    162                     } else {
    163                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
    164                     }
    165                     if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
    166                     request.result = hungUp;
    167                     // Wake up the requesting thread
    168                     synchronized (request) {
    169                         request.notifyAll();
    170                     }
    171                     break;
    172 
    173                 default:
    174                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
    175                     break;
    176             }
    177         }
    178     }
    179 
    180     /**
    181      * Posts the specified command to be executed on the main thread,
    182      * waits for the request to complete, and returns the result.
    183      * @see #sendRequestAsync
    184      */
    185     private Object sendRequest(int command, Object argument) {
    186         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
    187             throw new RuntimeException("This method will deadlock if called from the main thread.");
    188         }
    189 
    190         MainThreadRequest request = new MainThreadRequest(argument);
    191         Message msg = mMainThreadHandler.obtainMessage(command, request);
    192         msg.sendToTarget();
    193 
    194         // Wait for the request to complete
    195         synchronized (request) {
    196             while (request.result == null) {
    197                 try {
    198                     request.wait();
    199                 } catch (InterruptedException e) {
    200                     // Do nothing, go back and wait until the request is complete
    201                 }
    202             }
    203         }
    204         return request.result;
    205     }
    206 
    207     /**
    208      * Asynchronous ("fire and forget") version of sendRequest():
    209      * Posts the specified command to be executed on the main thread, and
    210      * returns immediately.
    211      * @see #sendRequest
    212      */
    213     private void sendRequestAsync(int command) {
    214         mMainThreadHandler.sendEmptyMessage(command);
    215     }
    216 
    217     /**
    218      * Initialize the singleton PhoneInterfaceManager instance.
    219      * This is only done once, at startup, from PhoneApp.onCreate().
    220      */
    221     /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {
    222         synchronized (PhoneInterfaceManager.class) {
    223             if (sInstance == null) {
    224                 sInstance = new PhoneInterfaceManager(app, phone);
    225             } else {
    226                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
    227             }
    228             return sInstance;
    229         }
    230     }
    231 
    232     /** Private constructor; @see init() */
    233     private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
    234         mApp = app;
    235         mPhone = phone;
    236         mCM = PhoneGlobals.getInstance().mCM;
    237         mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
    238         mMainThreadHandler = new MainThreadHandler();
    239         publish();
    240     }
    241 
    242     private void publish() {
    243         if (DBG) log("publish: " + this);
    244 
    245         ServiceManager.addService("phone", this);
    246     }
    247 
    248     //
    249     // Implementation of the ITelephony interface.
    250     //
    251 
    252     public void dial(String number) {
    253         if (DBG) log("dial: " + number);
    254         // No permission check needed here: This is just a wrapper around the
    255         // ACTION_DIAL intent, which is available to any app since it puts up
    256         // the UI before it does anything.
    257 
    258         String url = createTelUrl(number);
    259         if (url == null) {
    260             return;
    261         }
    262 
    263         // PENDING: should we just silently fail if phone is offhook or ringing?
    264         PhoneConstants.State state = mCM.getState();
    265         if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
    266             Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
    267             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    268             mApp.startActivity(intent);
    269         }
    270     }
    271 
    272     public void call(String callingPackage, String number) {
    273         if (DBG) log("call: " + number);
    274 
    275         // This is just a wrapper around the ACTION_CALL intent, but we still
    276         // need to do a permission check since we're calling startActivity()
    277         // from the context of the phone app.
    278         enforceCallPermission();
    279 
    280         if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
    281                 != AppOpsManager.MODE_ALLOWED) {
    282             return;
    283         }
    284 
    285         String url = createTelUrl(number);
    286         if (url == null) {
    287             return;
    288         }
    289 
    290         Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
    291         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    292         mApp.startActivity(intent);
    293     }
    294 
    295     private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
    296                                            boolean initialDialpadState) {
    297         if (!PhoneGlobals.sVoiceCapable) {
    298             // Never allow the InCallScreen to appear on data-only devices.
    299             return false;
    300         }
    301         if (isIdle()) {
    302             return false;
    303         }
    304         // If the phone isn't idle then go to the in-call screen
    305         long callingId = Binder.clearCallingIdentity();
    306         try {
    307             Intent intent;
    308             if (specifyInitialDialpadState) {
    309                 intent = PhoneGlobals.createInCallIntent(initialDialpadState);
    310             } else {
    311                 intent = PhoneGlobals.createInCallIntent();
    312             }
    313             try {
    314                 mApp.startActivity(intent);
    315             } catch (ActivityNotFoundException e) {
    316                 // It's possible that the in-call UI might not exist
    317                 // (like on non-voice-capable devices), although we
    318                 // shouldn't be trying to bring up the InCallScreen on
    319                 // devices like that in the first place!
    320                 Log.w(LOG_TAG, "showCallScreenInternal: "
    321                       + "transition to InCallScreen failed; intent = " + intent);
    322             }
    323         } finally {
    324             Binder.restoreCallingIdentity(callingId);
    325         }
    326         return true;
    327     }
    328 
    329     // Show the in-call screen without specifying the initial dialpad state.
    330     public boolean showCallScreen() {
    331         return showCallScreenInternal(false, false);
    332     }
    333 
    334     // The variation of showCallScreen() that specifies the initial dialpad state.
    335     // (Ideally this would be called showCallScreen() too, just with a different
    336     // signature, but AIDL doesn't allow that.)
    337     public boolean showCallScreenWithDialpad(boolean showDialpad) {
    338         return showCallScreenInternal(true, showDialpad);
    339     }
    340 
    341     /**
    342      * End a call based on call state
    343      * @return true is a call was ended
    344      */
    345     public boolean endCall() {
    346         enforceCallPermission();
    347         return (Boolean) sendRequest(CMD_END_CALL, null);
    348     }
    349 
    350     public void answerRingingCall() {
    351         if (DBG) log("answerRingingCall...");
    352         // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
    353         // but that can probably wait till the big TelephonyManager API overhaul.
    354         // For now, protect this call with the MODIFY_PHONE_STATE permission.
    355         enforceModifyPermission();
    356         sendRequestAsync(CMD_ANSWER_RINGING_CALL);
    357     }
    358 
    359     /**
    360      * Make the actual telephony calls to implement answerRingingCall().
    361      * This should only be called from the main thread of the Phone app.
    362      * @see #answerRingingCall
    363      *
    364      * TODO: it would be nice to return true if we answered the call, or
    365      * false if there wasn't actually a ringing incoming call, or some
    366      * other error occurred.  (In other words, pass back the return value
    367      * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
    368      * But that would require calling this method via sendRequest() rather
    369      * than sendRequestAsync(), and right now we don't actually *need* that
    370      * return value, so let's just return void for now.
    371      */
    372     private void answerRingingCallInternal() {
    373         final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
    374         if (hasRingingCall) {
    375             final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
    376             final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
    377             if (hasActiveCall && hasHoldingCall) {
    378                 // Both lines are in use!
    379                 // TODO: provide a flag to let the caller specify what
    380                 // policy to use if both lines are in use.  (The current
    381                 // behavior is hardwired to "answer incoming, end ongoing",
    382                 // which is how the CALL button is specced to behave.)
    383                 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
    384                 return;
    385             } else {
    386                 // answerCall() will automatically hold the current active
    387                 // call, if there is one.
    388                 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
    389                 return;
    390             }
    391         } else {
    392             // No call was ringing.
    393             return;
    394         }
    395     }
    396 
    397     public void silenceRinger() {
    398         if (DBG) log("silenceRinger...");
    399         // TODO: find a more appropriate permission to check here.
    400         // (That can probably wait till the big TelephonyManager API overhaul.
    401         // For now, protect this call with the MODIFY_PHONE_STATE permission.)
    402         enforceModifyPermission();
    403         sendRequestAsync(CMD_SILENCE_RINGER);
    404     }
    405 
    406     /**
    407      * Internal implemenation of silenceRinger().
    408      * This should only be called from the main thread of the Phone app.
    409      * @see #silenceRinger
    410      */
    411     private void silenceRingerInternal() {
    412         if ((mCM.getState() == PhoneConstants.State.RINGING)
    413             && mApp.notifier.isRinging()) {
    414             // Ringer is actually playing, so silence it.
    415             if (DBG) log("silenceRingerInternal: silencing...");
    416             mApp.notifier.silenceRinger();
    417         }
    418     }
    419 
    420     public boolean isOffhook() {
    421         return (mCM.getState() == PhoneConstants.State.OFFHOOK);
    422     }
    423 
    424     public boolean isRinging() {
    425         return (mCM.getState() == PhoneConstants.State.RINGING);
    426     }
    427 
    428     public boolean isIdle() {
    429         return (mCM.getState() == PhoneConstants.State.IDLE);
    430     }
    431 
    432     public boolean isSimPinEnabled() {
    433         enforceReadPermission();
    434         return (PhoneGlobals.getInstance().isSimPinEnabled());
    435     }
    436 
    437     public boolean supplyPin(String pin) {
    438         enforceModifyPermission();
    439         final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
    440         checkSimPin.start();
    441         return checkSimPin.unlockSim(null, pin);
    442     }
    443 
    444     public boolean supplyPuk(String puk, String pin) {
    445         enforceModifyPermission();
    446         final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
    447         checkSimPuk.start();
    448         return checkSimPuk.unlockSim(puk, pin);
    449     }
    450 
    451     /**
    452      * Helper thread to turn async call to {@link SimCard#supplyPin} into
    453      * a synchronous one.
    454      */
    455     private static class UnlockSim extends Thread {
    456 
    457         private final IccCard mSimCard;
    458 
    459         private boolean mDone = false;
    460         private boolean mResult = false;
    461 
    462         // For replies from SimCard interface
    463         private Handler mHandler;
    464 
    465         // For async handler to identify request type
    466         private static final int SUPPLY_PIN_COMPLETE = 100;
    467 
    468         public UnlockSim(IccCard simCard) {
    469             mSimCard = simCard;
    470         }
    471 
    472         @Override
    473         public void run() {
    474             Looper.prepare();
    475             synchronized (UnlockSim.this) {
    476                 mHandler = new Handler() {
    477                     @Override
    478                     public void handleMessage(Message msg) {
    479                         AsyncResult ar = (AsyncResult) msg.obj;
    480                         switch (msg.what) {
    481                             case SUPPLY_PIN_COMPLETE:
    482                                 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
    483                                 synchronized (UnlockSim.this) {
    484                                     mResult = (ar.exception == null);
    485                                     mDone = true;
    486                                     UnlockSim.this.notifyAll();
    487                                 }
    488                                 break;
    489                         }
    490                     }
    491                 };
    492                 UnlockSim.this.notifyAll();
    493             }
    494             Looper.loop();
    495         }
    496 
    497         /*
    498          * Use PIN or PUK to unlock SIM card
    499          *
    500          * If PUK is null, unlock SIM card with PIN
    501          *
    502          * If PUK is not null, unlock SIM card with PUK and set PIN code
    503          */
    504         synchronized boolean unlockSim(String puk, String pin) {
    505 
    506             while (mHandler == null) {
    507                 try {
    508                     wait();
    509                 } catch (InterruptedException e) {
    510                     Thread.currentThread().interrupt();
    511                 }
    512             }
    513             Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
    514 
    515             if (puk == null) {
    516                 mSimCard.supplyPin(pin, callback);
    517             } else {
    518                 mSimCard.supplyPuk(puk, pin, callback);
    519             }
    520 
    521             while (!mDone) {
    522                 try {
    523                     Log.d(LOG_TAG, "wait for done");
    524                     wait();
    525                 } catch (InterruptedException e) {
    526                     // Restore the interrupted status
    527                     Thread.currentThread().interrupt();
    528                 }
    529             }
    530             Log.d(LOG_TAG, "done");
    531             return mResult;
    532         }
    533     }
    534 
    535     public void updateServiceLocation() {
    536         // No permission check needed here: this call is harmless, and it's
    537         // needed for the ServiceState.requestStateUpdate() call (which is
    538         // already intentionally exposed to 3rd parties.)
    539         mPhone.updateServiceLocation();
    540     }
    541 
    542     public boolean isRadioOn() {
    543         return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
    544     }
    545 
    546     public void toggleRadioOnOff() {
    547         enforceModifyPermission();
    548         mPhone.setRadioPower(!isRadioOn());
    549     }
    550     public boolean setRadio(boolean turnOn) {
    551         enforceModifyPermission();
    552         if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
    553             toggleRadioOnOff();
    554         }
    555         return true;
    556     }
    557     public boolean setRadioPower(boolean turnOn) {
    558         enforceModifyPermission();
    559         mPhone.setRadioPower(turnOn);
    560         return true;
    561     }
    562 
    563     public boolean enableDataConnectivity() {
    564         enforceModifyPermission();
    565         ConnectivityManager cm =
    566                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
    567         cm.setMobileDataEnabled(true);
    568         return true;
    569     }
    570 
    571     public int enableApnType(String type) {
    572         enforceModifyPermission();
    573         return mPhone.enableApnType(type);
    574     }
    575 
    576     public int disableApnType(String type) {
    577         enforceModifyPermission();
    578         return mPhone.disableApnType(type);
    579     }
    580 
    581     public boolean disableDataConnectivity() {
    582         enforceModifyPermission();
    583         ConnectivityManager cm =
    584                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
    585         cm.setMobileDataEnabled(false);
    586         return true;
    587     }
    588 
    589     public boolean isDataConnectivityPossible() {
    590         return mPhone.isDataConnectivityPossible();
    591     }
    592 
    593     public boolean handlePinMmi(String dialString) {
    594         enforceModifyPermission();
    595         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
    596     }
    597 
    598     public void cancelMissedCallsNotification() {
    599         enforceModifyPermission();
    600         mApp.notificationMgr.cancelMissedCallNotification();
    601     }
    602 
    603     public int getCallState() {
    604         return DefaultPhoneNotifier.convertCallState(mCM.getState());
    605     }
    606 
    607     public int getDataState() {
    608         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
    609     }
    610 
    611     public int getDataActivity() {
    612         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
    613     }
    614 
    615     @Override
    616     public Bundle getCellLocation() {
    617         try {
    618             mApp.enforceCallingOrSelfPermission(
    619                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    620         } catch (SecurityException e) {
    621             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
    622             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
    623             // is the weaker precondition
    624             mApp.enforceCallingOrSelfPermission(
    625                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    626         }
    627 
    628         if (checkIfCallerIsSelfOrForegoundUser()) {
    629             if (DBG_LOC) log("getCellLocation: is active user");
    630             Bundle data = new Bundle();
    631             mPhone.getCellLocation().fillInNotifierBundle(data);
    632             return data;
    633         } else {
    634             if (DBG_LOC) log("getCellLocation: suppress non-active user");
    635             return null;
    636         }
    637     }
    638 
    639     @Override
    640     public void enableLocationUpdates() {
    641         mApp.enforceCallingOrSelfPermission(
    642                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
    643         mPhone.enableLocationUpdates();
    644     }
    645 
    646     @Override
    647     public void disableLocationUpdates() {
    648         mApp.enforceCallingOrSelfPermission(
    649                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
    650         mPhone.disableLocationUpdates();
    651     }
    652 
    653     @Override
    654     @SuppressWarnings("unchecked")
    655     public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
    656         try {
    657             mApp.enforceCallingOrSelfPermission(
    658                     android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    659         } catch (SecurityException e) {
    660             // If we have ACCESS_FINE_LOCATION permission, skip the check
    661             // for ACCESS_COARSE_LOCATION
    662             // A failure should throw the SecurityException from
    663             // ACCESS_COARSE_LOCATION since this is the weaker precondition
    664             mApp.enforceCallingOrSelfPermission(
    665                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    666         }
    667 
    668         if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
    669                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    670             return null;
    671         }
    672         if (checkIfCallerIsSelfOrForegoundUser()) {
    673             if (DBG_LOC) log("getNeighboringCellInfo: is active user");
    674 
    675             ArrayList<NeighboringCellInfo> cells = null;
    676 
    677             try {
    678                 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
    679                         CMD_HANDLE_NEIGHBORING_CELL, null);
    680             } catch (RuntimeException e) {
    681                 Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
    682             }
    683             return cells;
    684         } else {
    685             if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
    686             return null;
    687         }
    688     }
    689 
    690 
    691     @Override
    692     public List<CellInfo> getAllCellInfo() {
    693         try {
    694             mApp.enforceCallingOrSelfPermission(
    695                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
    696         } catch (SecurityException e) {
    697             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
    698             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
    699             // is the weaker precondition
    700             mApp.enforceCallingOrSelfPermission(
    701                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
    702         }
    703 
    704         if (checkIfCallerIsSelfOrForegoundUser()) {
    705             if (DBG_LOC) log("getAllCellInfo: is active user");
    706             return mPhone.getAllCellInfo();
    707         } else {
    708             if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
    709             return null;
    710         }
    711     }
    712 
    713     public void setCellInfoListRate(int rateInMillis) {
    714         mPhone.setCellInfoListRate(rateInMillis);
    715     }
    716 
    717     //
    718     // Internal helper methods.
    719     //
    720 
    721     private boolean checkIfCallerIsSelfOrForegoundUser() {
    722         boolean ok;
    723 
    724         boolean self = Binder.getCallingUid() == Process.myUid();
    725         if (!self) {
    726             // Get the caller's user id then clear the calling identity
    727             // which will be restored in the finally clause.
    728             int callingUser = UserHandle.getCallingUserId();
    729             long ident = Binder.clearCallingIdentity();
    730 
    731             try {
    732                 // With calling identity cleared the current user is the foreground user.
    733                 int foregroundUser = ActivityManager.getCurrentUser();
    734                 ok = (foregroundUser == callingUser);
    735                 if (DBG_LOC) {
    736                     log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
    737                             + " callingUser=" + callingUser + " ok=" + ok);
    738                 }
    739             } catch (Exception ex) {
    740                 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
    741                 ok = false;
    742             } finally {
    743                 Binder.restoreCallingIdentity(ident);
    744             }
    745         } else {
    746             if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
    747             ok = true;
    748         }
    749         if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
    750         return ok;
    751     }
    752 
    753     /**
    754      * Make sure the caller has the READ_PHONE_STATE permission.
    755      *
    756      * @throws SecurityException if the caller does not have the required permission
    757      */
    758     private void enforceReadPermission() {
    759         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
    760     }
    761 
    762     /**
    763      * Make sure the caller has the MODIFY_PHONE_STATE permission.
    764      *
    765      * @throws SecurityException if the caller does not have the required permission
    766      */
    767     private void enforceModifyPermission() {
    768         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
    769     }
    770 
    771     /**
    772      * Make sure the caller has the CALL_PHONE permission.
    773      *
    774      * @throws SecurityException if the caller does not have the required permission
    775      */
    776     private void enforceCallPermission() {
    777         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
    778     }
    779 
    780 
    781     private String createTelUrl(String number) {
    782         if (TextUtils.isEmpty(number)) {
    783             return null;
    784         }
    785 
    786         StringBuilder buf = new StringBuilder("tel:");
    787         buf.append(number);
    788         return buf.toString();
    789     }
    790 
    791     private void log(String msg) {
    792         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
    793     }
    794 
    795     private void loge(String msg) {
    796         Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
    797     }
    798 
    799     public int getActivePhoneType() {
    800         return mPhone.getPhoneType();
    801     }
    802 
    803     /**
    804      * Returns the CDMA ERI icon index to display
    805      */
    806     public int getCdmaEriIconIndex() {
    807         return mPhone.getCdmaEriIconIndex();
    808     }
    809 
    810     /**
    811      * Returns the CDMA ERI icon mode,
    812      * 0 - ON
    813      * 1 - FLASHING
    814      */
    815     public int getCdmaEriIconMode() {
    816         return mPhone.getCdmaEriIconMode();
    817     }
    818 
    819     /**
    820      * Returns the CDMA ERI text,
    821      */
    822     public String getCdmaEriText() {
    823         return mPhone.getCdmaEriText();
    824     }
    825 
    826     /**
    827      * Returns true if CDMA provisioning needs to run.
    828      */
    829     public boolean needsOtaServiceProvisioning() {
    830         return mPhone.needsOtaServiceProvisioning();
    831     }
    832 
    833     /**
    834      * Returns the unread count of voicemails
    835      */
    836     public int getVoiceMessageCount() {
    837         return mPhone.getVoiceMessageCount();
    838     }
    839 
    840     /**
    841      * Returns the data network type
    842      *
    843      * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
    844      */
    845     @Override
    846     public int getNetworkType() {
    847         return mPhone.getServiceState().getDataNetworkType();
    848     }
    849 
    850     /**
    851      * Returns the data network type
    852      */
    853     @Override
    854     public int getDataNetworkType() {
    855         return mPhone.getServiceState().getDataNetworkType();
    856     }
    857 
    858     /**
    859      * Returns the data network type
    860      */
    861     @Override
    862     public int getVoiceNetworkType() {
    863         return mPhone.getServiceState().getVoiceNetworkType();
    864     }
    865 
    866     /**
    867      * @return true if a ICC card is present
    868      */
    869     public boolean hasIccCard() {
    870         return mPhone.getIccCard().hasIccCard();
    871     }
    872 
    873     /**
    874      * Return if the current radio is LTE on CDMA. This
    875      * is a tri-state return value as for a period of time
    876      * the mode may be unknown.
    877      *
    878      * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
    879      * or {@link PHone#LTE_ON_CDMA_TRUE}
    880      */
    881     public int getLteOnCdmaMode() {
    882         return mPhone.getLteOnCdmaMode();
    883     }
    884 }
    885