• 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.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 
MainThreadRequest(Object argument)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
handleMessage(Message msg)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      */
sendRequest(int command, Object argument)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      */
sendRequestAsync(int command)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      */
init(PhoneGlobals app, Phone phone)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() */
PhoneInterfaceManager(PhoneGlobals app, Phone phone)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 
publish()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 
dial(String number)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 
call(String number)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 
showCallScreenInternal(boolean specifyInitialDialpadState, boolean initialDialpadState)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.
showCallScreen()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.)
showCallScreenWithDialpad(boolean showDialpad)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      */
endCall()337     public boolean endCall() {
338         enforceCallPermission();
339         return (Boolean) sendRequest(CMD_END_CALL, null);
340     }
341 
answerRingingCall()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      */
answerRingingCallInternal()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 
silenceRinger()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      */
silenceRingerInternal()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 
isOffhook()412     public boolean isOffhook() {
413         return (mCM.getState() == PhoneConstants.State.OFFHOOK);
414     }
415 
isRinging()416     public boolean isRinging() {
417         return (mCM.getState() == PhoneConstants.State.RINGING);
418     }
419 
isIdle()420     public boolean isIdle() {
421         return (mCM.getState() == PhoneConstants.State.IDLE);
422     }
423 
isSimPinEnabled()424     public boolean isSimPinEnabled() {
425         enforceReadPermission();
426         return (PhoneGlobals.getInstance().isSimPinEnabled());
427     }
428 
supplyPin(String pin)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 
supplyPuk(String puk, String pin)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 
UnlockSim(IccCard simCard)460         public UnlockSim(IccCard simCard) {
461             mSimCard = simCard;
462         }
463 
464         @Override
run()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          */
unlockSim(String puk, String pin)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 
updateServiceLocation()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 
isRadioOn()534     public boolean isRadioOn() {
535         return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
536     }
537 
toggleRadioOnOff()538     public void toggleRadioOnOff() {
539         enforceModifyPermission();
540         mPhone.setRadioPower(!isRadioOn());
541     }
setRadio(boolean turnOn)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 
enableDataConnectivity()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 
enableApnType(String type)558     public int enableApnType(String type) {
559         enforceModifyPermission();
560         return mPhone.enableApnType(type);
561     }
562 
disableApnType(String type)563     public int disableApnType(String type) {
564         enforceModifyPermission();
565         return mPhone.disableApnType(type);
566     }
567 
disableDataConnectivity()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 
isDataConnectivityPossible()576     public boolean isDataConnectivityPossible() {
577         return mPhone.isDataConnectivityPossible();
578     }
579 
handlePinMmi(String dialString)580     public boolean handlePinMmi(String dialString) {
581         enforceModifyPermission();
582         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
583     }
584 
cancelMissedCallsNotification()585     public void cancelMissedCallsNotification() {
586         enforceModifyPermission();
587         mApp.notificationMgr.cancelMissedCallNotification();
588     }
589 
getCallState()590     public int getCallState() {
591         return DefaultPhoneNotifier.convertCallState(mCM.getState());
592     }
593 
getDataState()594     public int getDataState() {
595         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
596     }
597 
getDataActivity()598     public int getDataActivity() {
599         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
600     }
601 
602     @Override
getCellLocation()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
enableLocationUpdates()627     public void enableLocationUpdates() {
628         mApp.enforceCallingOrSelfPermission(
629                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
630         mPhone.enableLocationUpdates();
631     }
632 
633     @Override
disableLocationUpdates()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")
getNeighboringCellInfo()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
getAllCellInfo()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 
checkIfCallerIsSelfOrForegoundUser()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      */
enforceReadPermission()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      */
enforceModifyPermission()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      */
enforceCallPermission()755     private void enforceCallPermission() {
756         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
757     }
758 
759 
createTelUrl(String number)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 
log(String msg)770     private void log(String msg) {
771         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
772     }
773 
loge(String msg)774     private void loge(String msg) {
775         Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
776     }
777 
getActivePhoneType()778     public int getActivePhoneType() {
779         return mPhone.getPhoneType();
780     }
781 
782     /**
783      * Returns the CDMA ERI icon index to display
784      */
getCdmaEriIconIndex()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      */
getCdmaEriIconMode()794     public int getCdmaEriIconMode() {
795         return mPhone.getCdmaEriIconMode();
796     }
797 
798     /**
799      * Returns the CDMA ERI text,
800      */
getCdmaEriText()801     public String getCdmaEriText() {
802         return mPhone.getCdmaEriText();
803     }
804 
805     /**
806      * Returns true if CDMA provisioning needs to run.
807      */
needsOtaServiceProvisioning()808     public boolean needsOtaServiceProvisioning() {
809         return mPhone.needsOtaServiceProvisioning();
810     }
811 
812     /**
813      * Returns the unread count of voicemails
814      */
getVoiceMessageCount()815     public int getVoiceMessageCount() {
816         return mPhone.getVoiceMessageCount();
817     }
818 
819     /**
820      * Returns the network type
821      */
getNetworkType()822     public int getNetworkType() {
823         return mPhone.getServiceState().getNetworkType();
824     }
825 
826     /**
827      * @return true if a ICC card is present
828      */
hasIccCard()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      */
getLteOnCdmaMode()841     public int getLteOnCdmaMode() {
842         return mPhone.getLteOnCdmaMode();
843     }
844 }
845