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