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