• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.phone;
18 
19 import android.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 
MainThreadRequest(Object argument)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
handleMessage(Message msg)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      */
sendRequest(int command, Object argument)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      */
sendRequestAsync(int command)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      */
init(PhoneGlobals app, Phone phone)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() */
PhoneInterfaceManager(PhoneGlobals app, Phone phone)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 
publish()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 
dial(String number)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 
call(String callingPackage, String number)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 
showCallScreenInternal(boolean specifyInitialDialpadState, boolean initialDialpadState)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.
showCallScreen()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.)
showCallScreenWithDialpad(boolean showDialpad)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      */
endCall()345     public boolean endCall() {
346         enforceCallPermission();
347         return (Boolean) sendRequest(CMD_END_CALL, null);
348     }
349 
answerRingingCall()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      */
answerRingingCallInternal()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 
silenceRinger()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      */
silenceRingerInternal()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 
isOffhook()420     public boolean isOffhook() {
421         return (mCM.getState() == PhoneConstants.State.OFFHOOK);
422     }
423 
isRinging()424     public boolean isRinging() {
425         return (mCM.getState() == PhoneConstants.State.RINGING);
426     }
427 
isIdle()428     public boolean isIdle() {
429         return (mCM.getState() == PhoneConstants.State.IDLE);
430     }
431 
isSimPinEnabled()432     public boolean isSimPinEnabled() {
433         enforceReadPermission();
434         return (PhoneGlobals.getInstance().isSimPinEnabled());
435     }
436 
supplyPin(String pin)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 
supplyPuk(String puk, String pin)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 
UnlockSim(IccCard simCard)468         public UnlockSim(IccCard simCard) {
469             mSimCard = simCard;
470         }
471 
472         @Override
run()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          */
unlockSim(String puk, String pin)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 
updateServiceLocation()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 
isRadioOn()542     public boolean isRadioOn() {
543         return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
544     }
545 
toggleRadioOnOff()546     public void toggleRadioOnOff() {
547         enforceModifyPermission();
548         mPhone.setRadioPower(!isRadioOn());
549     }
setRadio(boolean turnOn)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     }
setRadioPower(boolean turnOn)557     public boolean setRadioPower(boolean turnOn) {
558         enforceModifyPermission();
559         mPhone.setRadioPower(turnOn);
560         return true;
561     }
562 
enableDataConnectivity()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 
enableApnType(String type)571     public int enableApnType(String type) {
572         enforceModifyPermission();
573         return mPhone.enableApnType(type);
574     }
575 
disableApnType(String type)576     public int disableApnType(String type) {
577         enforceModifyPermission();
578         return mPhone.disableApnType(type);
579     }
580 
disableDataConnectivity()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 
isDataConnectivityPossible()589     public boolean isDataConnectivityPossible() {
590         return mPhone.isDataConnectivityPossible();
591     }
592 
handlePinMmi(String dialString)593     public boolean handlePinMmi(String dialString) {
594         enforceModifyPermission();
595         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
596     }
597 
cancelMissedCallsNotification()598     public void cancelMissedCallsNotification() {
599         enforceModifyPermission();
600         mApp.notificationMgr.cancelMissedCallNotification();
601     }
602 
getCallState()603     public int getCallState() {
604         return DefaultPhoneNotifier.convertCallState(mCM.getState());
605     }
606 
getDataState()607     public int getDataState() {
608         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
609     }
610 
getDataActivity()611     public int getDataActivity() {
612         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
613     }
614 
615     @Override
getCellLocation()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
enableLocationUpdates()640     public void enableLocationUpdates() {
641         mApp.enforceCallingOrSelfPermission(
642                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
643         mPhone.enableLocationUpdates();
644     }
645 
646     @Override
disableLocationUpdates()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")
getNeighboringCellInfo(String callingPackage)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
getAllCellInfo()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 
setCellInfoListRate(int rateInMillis)713     public void setCellInfoListRate(int rateInMillis) {
714         mPhone.setCellInfoListRate(rateInMillis);
715     }
716 
717     //
718     // Internal helper methods.
719     //
720 
checkIfCallerIsSelfOrForegoundUser()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      */
enforceReadPermission()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      */
enforceModifyPermission()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      */
enforceCallPermission()776     private void enforceCallPermission() {
777         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
778     }
779 
780 
createTelUrl(String number)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 
log(String msg)791     private void log(String msg) {
792         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
793     }
794 
loge(String msg)795     private void loge(String msg) {
796         Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
797     }
798 
getActivePhoneType()799     public int getActivePhoneType() {
800         return mPhone.getPhoneType();
801     }
802 
803     /**
804      * Returns the CDMA ERI icon index to display
805      */
getCdmaEriIconIndex()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      */
getCdmaEriIconMode()815     public int getCdmaEriIconMode() {
816         return mPhone.getCdmaEriIconMode();
817     }
818 
819     /**
820      * Returns the CDMA ERI text,
821      */
getCdmaEriText()822     public String getCdmaEriText() {
823         return mPhone.getCdmaEriText();
824     }
825 
826     /**
827      * Returns true if CDMA provisioning needs to run.
828      */
needsOtaServiceProvisioning()829     public boolean needsOtaServiceProvisioning() {
830         return mPhone.needsOtaServiceProvisioning();
831     }
832 
833     /**
834      * Returns the unread count of voicemails
835      */
getVoiceMessageCount()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
getNetworkType()846     public int getNetworkType() {
847         return mPhone.getServiceState().getDataNetworkType();
848     }
849 
850     /**
851      * Returns the data network type
852      */
853     @Override
getDataNetworkType()854     public int getDataNetworkType() {
855         return mPhone.getServiceState().getDataNetworkType();
856     }
857 
858     /**
859      * Returns the data network type
860      */
861     @Override
getVoiceNetworkType()862     public int getVoiceNetworkType() {
863         return mPhone.getServiceState().getVoiceNetworkType();
864     }
865 
866     /**
867      * @return true if a ICC card is present
868      */
hasIccCard()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      */
getLteOnCdmaMode()881     public int getLteOnCdmaMode() {
882         return mPhone.getLteOnCdmaMode();
883     }
884 }
885