• 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.bluetooth.IBluetoothHeadsetPhone;
22 import android.content.ActivityNotFoundException;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.net.ConnectivityManager;
26 import android.net.Uri;
27 import android.os.AsyncResult;
28 import android.os.Binder;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.Process;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.UserHandle;
38 import android.telephony.NeighboringCellInfo;
39 import android.telephony.CellInfo;
40 import android.telephony.ServiceState;
41 import android.text.TextUtils;
42 import android.util.Log;
43 
44 import com.android.internal.telephony.CallManager;
45 import com.android.internal.telephony.CommandException;
46 import com.android.internal.telephony.Connection;
47 import com.android.internal.telephony.DefaultPhoneNotifier;
48 import com.android.internal.telephony.IccCard;
49 import com.android.internal.telephony.ITelephony;
50 import com.android.internal.telephony.ITelephonyListener;
51 import com.android.internal.telephony.Phone;
52 import com.android.internal.telephony.PhoneConstants;
53 import com.android.services.telephony.common.Call;
54 
55 import com.android.internal.util.HexDump;
56 
57 import java.util.ArrayList;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Map;
62 
63 /**
64  * Implementation of the ITelephony interface.
65  */
66 public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
67     private static final String LOG_TAG = "PhoneInterfaceManager";
68     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
69     private static final boolean DBG_LOC = false;
70 
71     // Message codes used with mMainThreadHandler
72     private static final int CMD_HANDLE_PIN_MMI = 1;
73     private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
74     private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
75     private static final int CMD_ANSWER_RINGING_CALL = 4;
76     private static final int CMD_END_CALL = 5;  // not used yet
77     private static final int CMD_SILENCE_RINGER = 6;
78 
79     /** The singleton instance. */
80     private static PhoneInterfaceManager sInstance;
81 
82     PhoneGlobals mApp;
83     Phone mPhone;
84     CallManager mCM;
85     AppOpsManager mAppOps;
86     MainThreadHandler mMainThreadHandler;
87     CallHandlerServiceProxy mCallHandlerService;
88     CallModeler mCallModeler;
89     DTMFTonePlayer mDtmfTonePlayer;
90     Handler mDtmfStopHandler = new Handler();
91     Runnable mDtmfStopRunnable;
92 
93     private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
94     private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
95             new HashMap<IBinder, TelephonyListenerDeathRecipient>();
96 
97     /**
98      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
99      * request after sending. The main thread will notify the request when it is complete.
100      */
101     private static final class MainThreadRequest {
102         /** The argument to use for the request */
103         public Object argument;
104         /** The result of the request that is run on the main thread */
105         public Object result;
106 
MainThreadRequest(Object argument)107         public MainThreadRequest(Object argument) {
108             this.argument = argument;
109         }
110     }
111 
112     /**
113      * A handler that processes messages on the main thread in the phone process. Since many
114      * of the Phone calls are not thread safe this is needed to shuttle the requests from the
115      * inbound binder threads to the main thread in the phone process.  The Binder thread
116      * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
117      * on, which will be notified when the operation completes and will contain the result of the
118      * request.
119      *
120      * <p>If a MainThreadRequest object is provided in the msg.obj field,
121      * note that request.result must be set to something non-null for the calling thread to
122      * unblock.
123      */
124     private final class MainThreadHandler extends Handler {
125         @Override
handleMessage(Message msg)126         public void handleMessage(Message msg) {
127             MainThreadRequest request;
128             Message onCompleted;
129             AsyncResult ar;
130 
131             switch (msg.what) {
132                 case CMD_HANDLE_PIN_MMI:
133                     request = (MainThreadRequest) msg.obj;
134                     request.result = Boolean.valueOf(
135                             mPhone.handlePinMmi((String) request.argument));
136                     // Wake up the requesting thread
137                     synchronized (request) {
138                         request.notifyAll();
139                     }
140                     break;
141 
142                 case CMD_HANDLE_NEIGHBORING_CELL:
143                     request = (MainThreadRequest) msg.obj;
144                     onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
145                             request);
146                     mPhone.getNeighboringCids(onCompleted);
147                     break;
148 
149                 case EVENT_NEIGHBORING_CELL_DONE:
150                     ar = (AsyncResult) msg.obj;
151                     request = (MainThreadRequest) ar.userObj;
152                     if (ar.exception == null && ar.result != null) {
153                         request.result = ar.result;
154                     } else {
155                         // create an empty list to notify the waiting thread
156                         request.result = new ArrayList<NeighboringCellInfo>();
157                     }
158                     // Wake up the requesting thread
159                     synchronized (request) {
160                         request.notifyAll();
161                     }
162                     break;
163 
164                 case CMD_ANSWER_RINGING_CALL:
165                     answerRingingCallInternal();
166                     break;
167 
168                 case CMD_SILENCE_RINGER:
169                     silenceRingerInternal();
170                     break;
171 
172                 case CMD_END_CALL:
173                     request = (MainThreadRequest) msg.obj;
174                     boolean hungUp = false;
175                     int phoneType = mPhone.getPhoneType();
176                     if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
177                         // CDMA: If the user presses the Power button we treat it as
178                         // ending the complete call session
179                         hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
180                     } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
181                         // GSM: End the call as per the Phone state
182                         hungUp = PhoneUtils.hangup(mCM);
183                     } else {
184                         throw new IllegalStateException("Unexpected phone type: " + phoneType);
185                     }
186                     if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
187                     request.result = hungUp;
188                     // Wake up the requesting thread
189                     synchronized (request) {
190                         request.notifyAll();
191                     }
192                     break;
193 
194                 default:
195                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
196                     break;
197             }
198         }
199     }
200 
201     /**
202      * Posts the specified command to be executed on the main thread,
203      * waits for the request to complete, and returns the result.
204      * @see #sendRequestAsync
205      */
sendRequest(int command, Object argument)206     private Object sendRequest(int command, Object argument) {
207         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
208             throw new RuntimeException("This method will deadlock if called from the main thread.");
209         }
210 
211         MainThreadRequest request = new MainThreadRequest(argument);
212         Message msg = mMainThreadHandler.obtainMessage(command, request);
213         msg.sendToTarget();
214 
215         // Wait for the request to complete
216         synchronized (request) {
217             while (request.result == null) {
218                 try {
219                     request.wait();
220                 } catch (InterruptedException e) {
221                     // Do nothing, go back and wait until the request is complete
222                 }
223             }
224         }
225         return request.result;
226     }
227 
228     /**
229      * Asynchronous ("fire and forget") version of sendRequest():
230      * Posts the specified command to be executed on the main thread, and
231      * returns immediately.
232      * @see #sendRequest
233      */
sendRequestAsync(int command)234     private void sendRequestAsync(int command) {
235         mMainThreadHandler.sendEmptyMessage(command);
236     }
237 
238     /**
239      * Initialize the singleton PhoneInterfaceManager instance.
240      * This is only done once, at startup, from PhoneApp.onCreate().
241      */
init(PhoneGlobals app, Phone phone, CallHandlerServiceProxy callHandlerService, CallModeler callModeler, DTMFTonePlayer dtmfTonePlayer)242     /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
243                 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
244                 DTMFTonePlayer dtmfTonePlayer) {
245         synchronized (PhoneInterfaceManager.class) {
246             if (sInstance == null) {
247                 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
248                         dtmfTonePlayer);
249             } else {
250                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
251             }
252             return sInstance;
253         }
254     }
255 
256     /** Private constructor; @see init() */
PhoneInterfaceManager(PhoneGlobals app, Phone phone, CallHandlerServiceProxy callHandlerService, CallModeler callModeler, DTMFTonePlayer dtmfTonePlayer)257     private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
258             CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
259             DTMFTonePlayer dtmfTonePlayer) {
260         mApp = app;
261         mPhone = phone;
262         mCM = PhoneGlobals.getInstance().mCM;
263         mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
264         mMainThreadHandler = new MainThreadHandler();
265         mCallHandlerService = callHandlerService;
266         mCallModeler = callModeler;
267         mCallModeler.addListener(this);
268         mDtmfTonePlayer = dtmfTonePlayer;
269         publish();
270     }
271 
publish()272     private void publish() {
273         if (DBG) log("publish: " + this);
274 
275         ServiceManager.addService("phone", this);
276     }
277 
278     //
279     // Implementation of the ITelephony interface.
280     //
281 
dial(String number)282     public void dial(String number) {
283         if (DBG) log("dial: " + number);
284         // No permission check needed here: This is just a wrapper around the
285         // ACTION_DIAL intent, which is available to any app since it puts up
286         // the UI before it does anything.
287 
288         String url = createTelUrl(number);
289         if (url == null) {
290             return;
291         }
292 
293         // PENDING: should we just silently fail if phone is offhook or ringing?
294         PhoneConstants.State state = mCM.getState();
295         if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
296             Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
297             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
298             mApp.startActivity(intent);
299         }
300     }
301 
call(String callingPackage, String number)302     public void call(String callingPackage, String number) {
303         if (DBG) log("call: " + number);
304 
305         // This is just a wrapper around the ACTION_CALL intent, but we still
306         // need to do a permission check since we're calling startActivity()
307         // from the context of the phone app.
308         enforceCallPermission();
309 
310         if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
311                 != AppOpsManager.MODE_ALLOWED) {
312             return;
313         }
314 
315         String url = createTelUrl(number);
316         if (url == null) {
317             return;
318         }
319 
320         Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
321         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
322         mApp.startActivity(intent);
323     }
324 
showCallScreenInternal(boolean specifyInitialDialpadState, boolean showDialpad)325     private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
326                                            boolean showDialpad) {
327         if (!PhoneGlobals.sVoiceCapable) {
328             // Never allow the InCallScreen to appear on data-only devices.
329             return false;
330         }
331         if (isIdle()) {
332             return false;
333         }
334         // If the phone isn't idle then go to the in-call screen
335         long callingId = Binder.clearCallingIdentity();
336 
337         mCallHandlerService.bringToForeground(showDialpad);
338 
339         Binder.restoreCallingIdentity(callingId);
340         return true;
341     }
342 
343     // Show the in-call screen without specifying the initial dialpad state.
showCallScreen()344     public boolean showCallScreen() {
345         return showCallScreenInternal(false, false);
346     }
347 
348     // The variation of showCallScreen() that specifies the initial dialpad state.
349     // (Ideally this would be called showCallScreen() too, just with a different
350     // signature, but AIDL doesn't allow that.)
showCallScreenWithDialpad(boolean showDialpad)351     public boolean showCallScreenWithDialpad(boolean showDialpad) {
352         return showCallScreenInternal(true, showDialpad);
353     }
354 
355     /**
356      * End a call based on call state
357      * @return true is a call was ended
358      */
endCall()359     public boolean endCall() {
360         enforceCallPermission();
361         return (Boolean) sendRequest(CMD_END_CALL, null);
362     }
363 
answerRingingCall()364     public void answerRingingCall() {
365         if (DBG) log("answerRingingCall...");
366         // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
367         // but that can probably wait till the big TelephonyManager API overhaul.
368         // For now, protect this call with the MODIFY_PHONE_STATE permission.
369         enforceModifyPermission();
370         sendRequestAsync(CMD_ANSWER_RINGING_CALL);
371     }
372 
373     /**
374      * Make the actual telephony calls to implement answerRingingCall().
375      * This should only be called from the main thread of the Phone app.
376      * @see #answerRingingCall
377      *
378      * TODO: it would be nice to return true if we answered the call, or
379      * false if there wasn't actually a ringing incoming call, or some
380      * other error occurred.  (In other words, pass back the return value
381      * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
382      * But that would require calling this method via sendRequest() rather
383      * than sendRequestAsync(), and right now we don't actually *need* that
384      * return value, so let's just return void for now.
385      */
answerRingingCallInternal()386     private void answerRingingCallInternal() {
387         final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
388         if (hasRingingCall) {
389             final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
390             final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
391             if (hasActiveCall && hasHoldingCall) {
392                 // Both lines are in use!
393                 // TODO: provide a flag to let the caller specify what
394                 // policy to use if both lines are in use.  (The current
395                 // behavior is hardwired to "answer incoming, end ongoing",
396                 // which is how the CALL button is specced to behave.)
397                 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
398                 return;
399             } else {
400                 // answerCall() will automatically hold the current active
401                 // call, if there is one.
402                 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
403                 return;
404             }
405         } else {
406             // No call was ringing.
407             return;
408         }
409     }
410 
silenceRinger()411     public void silenceRinger() {
412         if (DBG) log("silenceRinger...");
413         // TODO: find a more appropriate permission to check here.
414         // (That can probably wait till the big TelephonyManager API overhaul.
415         // For now, protect this call with the MODIFY_PHONE_STATE permission.)
416         enforceModifyPermission();
417         sendRequestAsync(CMD_SILENCE_RINGER);
418     }
419 
420     /**
421      * Internal implemenation of silenceRinger().
422      * This should only be called from the main thread of the Phone app.
423      * @see #silenceRinger
424      */
silenceRingerInternal()425     private void silenceRingerInternal() {
426         if ((mCM.getState() == PhoneConstants.State.RINGING)
427             && mApp.notifier.isRinging()) {
428             // Ringer is actually playing, so silence it.
429             if (DBG) log("silenceRingerInternal: silencing...");
430             mApp.notifier.silenceRinger();
431         }
432     }
433 
isOffhook()434     public boolean isOffhook() {
435         return (mCM.getState() == PhoneConstants.State.OFFHOOK);
436     }
437 
isRinging()438     public boolean isRinging() {
439         return (mCM.getState() == PhoneConstants.State.RINGING);
440     }
441 
isIdle()442     public boolean isIdle() {
443         return (mCM.getState() == PhoneConstants.State.IDLE);
444     }
445 
isSimPinEnabled()446     public boolean isSimPinEnabled() {
447         enforceReadPermission();
448         return (PhoneGlobals.getInstance().isSimPinEnabled());
449     }
450 
supplyPin(String pin)451     public boolean supplyPin(String pin) {
452         int [] resultArray = supplyPinReportResult(pin);
453         return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
454     }
455 
supplyPuk(String puk, String pin)456     public boolean supplyPuk(String puk, String pin) {
457         int [] resultArray = supplyPukReportResult(puk, pin);
458         return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
459     }
460 
461     /** {@hide} */
supplyPinReportResult(String pin)462     public int[] supplyPinReportResult(String pin) {
463         enforceModifyPermission();
464         final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
465         checkSimPin.start();
466         return checkSimPin.unlockSim(null, pin);
467     }
468 
469     /** {@hide} */
supplyPukReportResult(String puk, String pin)470     public int[] supplyPukReportResult(String puk, String pin) {
471         enforceModifyPermission();
472         final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
473         checkSimPuk.start();
474         return checkSimPuk.unlockSim(puk, pin);
475     }
476 
477     /**
478      * Helper thread to turn async call to SimCard#supplyPin into
479      * a synchronous one.
480      */
481     private static class UnlockSim extends Thread {
482 
483         private final IccCard mSimCard;
484 
485         private boolean mDone = false;
486         private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
487         private int mRetryCount = -1;
488 
489         // For replies from SimCard interface
490         private Handler mHandler;
491 
492         // For async handler to identify request type
493         private static final int SUPPLY_PIN_COMPLETE = 100;
494 
UnlockSim(IccCard simCard)495         public UnlockSim(IccCard simCard) {
496             mSimCard = simCard;
497         }
498 
499         @Override
run()500         public void run() {
501             Looper.prepare();
502             synchronized (UnlockSim.this) {
503                 mHandler = new Handler() {
504                     @Override
505                     public void handleMessage(Message msg) {
506                         AsyncResult ar = (AsyncResult) msg.obj;
507                         switch (msg.what) {
508                             case SUPPLY_PIN_COMPLETE:
509                                 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
510                                 synchronized (UnlockSim.this) {
511                                     mRetryCount = msg.arg1;
512                                     if (ar.exception != null) {
513                                         if (ar.exception instanceof CommandException &&
514                                                 ((CommandException)(ar.exception)).getCommandError()
515                                                 == CommandException.Error.PASSWORD_INCORRECT) {
516                                             mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
517                                         } else {
518                                             mResult = PhoneConstants.PIN_GENERAL_FAILURE;
519                                         }
520                                     } else {
521                                         mResult = PhoneConstants.PIN_RESULT_SUCCESS;
522                                     }
523                                     mDone = true;
524                                     UnlockSim.this.notifyAll();
525                                 }
526                                 break;
527                         }
528                     }
529                 };
530                 UnlockSim.this.notifyAll();
531             }
532             Looper.loop();
533         }
534 
535         /*
536          * Use PIN or PUK to unlock SIM card
537          *
538          * If PUK is null, unlock SIM card with PIN
539          *
540          * If PUK is not null, unlock SIM card with PUK and set PIN code
541          */
unlockSim(String puk, String pin)542         synchronized int[] unlockSim(String puk, String pin) {
543 
544             while (mHandler == null) {
545                 try {
546                     wait();
547                 } catch (InterruptedException e) {
548                     Thread.currentThread().interrupt();
549                 }
550             }
551             Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
552 
553             if (puk == null) {
554                 mSimCard.supplyPin(pin, callback);
555             } else {
556                 mSimCard.supplyPuk(puk, pin, callback);
557             }
558 
559             while (!mDone) {
560                 try {
561                     Log.d(LOG_TAG, "wait for done");
562                     wait();
563                 } catch (InterruptedException e) {
564                     // Restore the interrupted status
565                     Thread.currentThread().interrupt();
566                 }
567             }
568             Log.d(LOG_TAG, "done");
569             int[] resultArray = new int[2];
570             resultArray[0] = mResult;
571             resultArray[1] = mRetryCount;
572             return resultArray;
573         }
574     }
575 
updateServiceLocation()576     public void updateServiceLocation() {
577         // No permission check needed here: this call is harmless, and it's
578         // needed for the ServiceState.requestStateUpdate() call (which is
579         // already intentionally exposed to 3rd parties.)
580         mPhone.updateServiceLocation();
581     }
582 
isRadioOn()583     public boolean isRadioOn() {
584         return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
585     }
586 
toggleRadioOnOff()587     public void toggleRadioOnOff() {
588         enforceModifyPermission();
589         mPhone.setRadioPower(!isRadioOn());
590     }
setRadio(boolean turnOn)591     public boolean setRadio(boolean turnOn) {
592         enforceModifyPermission();
593         if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
594             toggleRadioOnOff();
595         }
596         return true;
597     }
setRadioPower(boolean turnOn)598     public boolean setRadioPower(boolean turnOn) {
599         enforceModifyPermission();
600         mPhone.setRadioPower(turnOn);
601         return true;
602     }
603 
enableDataConnectivity()604     public boolean enableDataConnectivity() {
605         enforceModifyPermission();
606         ConnectivityManager cm =
607                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
608         cm.setMobileDataEnabled(true);
609         return true;
610     }
611 
enableApnType(String type)612     public int enableApnType(String type) {
613         enforceModifyPermission();
614         return mPhone.enableApnType(type);
615     }
616 
disableApnType(String type)617     public int disableApnType(String type) {
618         enforceModifyPermission();
619         return mPhone.disableApnType(type);
620     }
621 
disableDataConnectivity()622     public boolean disableDataConnectivity() {
623         enforceModifyPermission();
624         ConnectivityManager cm =
625                 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
626         cm.setMobileDataEnabled(false);
627         return true;
628     }
629 
isDataConnectivityPossible()630     public boolean isDataConnectivityPossible() {
631         return mPhone.isDataConnectivityPossible();
632     }
633 
handlePinMmi(String dialString)634     public boolean handlePinMmi(String dialString) {
635         enforceModifyPermission();
636         return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
637     }
638 
cancelMissedCallsNotification()639     public void cancelMissedCallsNotification() {
640         enforceModifyPermission();
641         mApp.notificationMgr.cancelMissedCallNotification();
642     }
643 
getCallState()644     public int getCallState() {
645         return DefaultPhoneNotifier.convertCallState(mCM.getState());
646     }
647 
getDataState()648     public int getDataState() {
649         return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
650     }
651 
getDataActivity()652     public int getDataActivity() {
653         return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
654     }
655 
656     @Override
getCellLocation()657     public Bundle getCellLocation() {
658         try {
659             mApp.enforceCallingOrSelfPermission(
660                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
661         } catch (SecurityException e) {
662             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
663             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
664             // is the weaker precondition
665             mApp.enforceCallingOrSelfPermission(
666                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
667         }
668 
669         if (checkIfCallerIsSelfOrForegoundUser()) {
670             if (DBG_LOC) log("getCellLocation: is active user");
671             Bundle data = new Bundle();
672             mPhone.getCellLocation().fillInNotifierBundle(data);
673             return data;
674         } else {
675             if (DBG_LOC) log("getCellLocation: suppress non-active user");
676             return null;
677         }
678     }
679 
680     @Override
enableLocationUpdates()681     public void enableLocationUpdates() {
682         mApp.enforceCallingOrSelfPermission(
683                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
684         mPhone.enableLocationUpdates();
685     }
686 
687     @Override
disableLocationUpdates()688     public void disableLocationUpdates() {
689         mApp.enforceCallingOrSelfPermission(
690                 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
691         mPhone.disableLocationUpdates();
692     }
693 
694     @Override
695     @SuppressWarnings("unchecked")
getNeighboringCellInfo(String callingPackage)696     public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
697         try {
698             mApp.enforceCallingOrSelfPermission(
699                     android.Manifest.permission.ACCESS_FINE_LOCATION, null);
700         } catch (SecurityException e) {
701             // If we have ACCESS_FINE_LOCATION permission, skip the check
702             // for ACCESS_COARSE_LOCATION
703             // A failure should throw the SecurityException from
704             // ACCESS_COARSE_LOCATION since this is the weaker precondition
705             mApp.enforceCallingOrSelfPermission(
706                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
707         }
708 
709         if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
710                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
711             return null;
712         }
713         if (checkIfCallerIsSelfOrForegoundUser()) {
714             if (DBG_LOC) log("getNeighboringCellInfo: is active user");
715 
716             ArrayList<NeighboringCellInfo> cells = null;
717 
718             try {
719                 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
720                         CMD_HANDLE_NEIGHBORING_CELL, null);
721             } catch (RuntimeException e) {
722                 Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
723             }
724             return cells;
725         } else {
726             if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
727             return null;
728         }
729     }
730 
731 
732     @Override
getAllCellInfo()733     public List<CellInfo> getAllCellInfo() {
734         try {
735             mApp.enforceCallingOrSelfPermission(
736                 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
737         } catch (SecurityException e) {
738             // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
739             // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
740             // is the weaker precondition
741             mApp.enforceCallingOrSelfPermission(
742                 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
743         }
744 
745         if (checkIfCallerIsSelfOrForegoundUser()) {
746             if (DBG_LOC) log("getAllCellInfo: is active user");
747             return mPhone.getAllCellInfo();
748         } else {
749             if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
750             return null;
751         }
752     }
753 
setCellInfoListRate(int rateInMillis)754     public void setCellInfoListRate(int rateInMillis) {
755         mPhone.setCellInfoListRate(rateInMillis);
756     }
757 
758     //
759     // Internal helper methods.
760     //
761 
checkIfCallerIsSelfOrForegoundUser()762     private boolean checkIfCallerIsSelfOrForegoundUser() {
763         boolean ok;
764 
765         boolean self = Binder.getCallingUid() == Process.myUid();
766         if (!self) {
767             // Get the caller's user id then clear the calling identity
768             // which will be restored in the finally clause.
769             int callingUser = UserHandle.getCallingUserId();
770             long ident = Binder.clearCallingIdentity();
771 
772             try {
773                 // With calling identity cleared the current user is the foreground user.
774                 int foregroundUser = ActivityManager.getCurrentUser();
775                 ok = (foregroundUser == callingUser);
776                 if (DBG_LOC) {
777                     log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
778                             + " callingUser=" + callingUser + " ok=" + ok);
779                 }
780             } catch (Exception ex) {
781                 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
782                 ok = false;
783             } finally {
784                 Binder.restoreCallingIdentity(ident);
785             }
786         } else {
787             if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
788             ok = true;
789         }
790         if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
791         return ok;
792     }
793 
794     /**
795      * Make sure the caller has the READ_PHONE_STATE permission.
796      *
797      * @throws SecurityException if the caller does not have the required permission
798      */
enforceReadPermission()799     private void enforceReadPermission() {
800         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
801     }
802 
803     /**
804      * Make sure the caller has the MODIFY_PHONE_STATE permission.
805      *
806      * @throws SecurityException if the caller does not have the required permission
807      */
enforceModifyPermission()808     private void enforceModifyPermission() {
809         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
810     }
811 
812     /**
813      * Make sure the caller has the CALL_PHONE permission.
814      *
815      * @throws SecurityException if the caller does not have the required permission
816      */
enforceCallPermission()817     private void enforceCallPermission() {
818         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
819     }
820 
821     /**
822      * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
823      *
824      * @throws SecurityException if the caller does not have the required permission
825      */
enforcePrivilegedPhoneStatePermission()826     private void enforcePrivilegedPhoneStatePermission() {
827         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
828                 null);
829     }
830 
createTelUrl(String number)831     private String createTelUrl(String number) {
832         if (TextUtils.isEmpty(number)) {
833             return null;
834         }
835 
836         StringBuilder buf = new StringBuilder("tel:");
837         buf.append(number);
838         return buf.toString();
839     }
840 
log(String msg)841     private void log(String msg) {
842         Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
843     }
844 
loge(String msg)845     private void loge(String msg) {
846         Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
847     }
848 
getActivePhoneType()849     public int getActivePhoneType() {
850         return mPhone.getPhoneType();
851     }
852 
853     /**
854      * Returns the CDMA ERI icon index to display
855      */
getCdmaEriIconIndex()856     public int getCdmaEriIconIndex() {
857         return mPhone.getCdmaEriIconIndex();
858     }
859 
860     /**
861      * Returns the CDMA ERI icon mode,
862      * 0 - ON
863      * 1 - FLASHING
864      */
getCdmaEriIconMode()865     public int getCdmaEriIconMode() {
866         return mPhone.getCdmaEriIconMode();
867     }
868 
869     /**
870      * Returns the CDMA ERI text,
871      */
getCdmaEriText()872     public String getCdmaEriText() {
873         return mPhone.getCdmaEriText();
874     }
875 
876     /**
877      * Returns true if CDMA provisioning needs to run.
878      */
needsOtaServiceProvisioning()879     public boolean needsOtaServiceProvisioning() {
880         return mPhone.needsOtaServiceProvisioning();
881     }
882 
883     /**
884      * Returns the unread count of voicemails
885      */
getVoiceMessageCount()886     public int getVoiceMessageCount() {
887         return mPhone.getVoiceMessageCount();
888     }
889 
890     /**
891      * Returns the data network type
892      *
893      * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
894      */
895     @Override
getNetworkType()896     public int getNetworkType() {
897         return mPhone.getServiceState().getDataNetworkType();
898     }
899 
900     /**
901      * Returns the data network type
902      */
903     @Override
getDataNetworkType()904     public int getDataNetworkType() {
905         return mPhone.getServiceState().getDataNetworkType();
906     }
907 
908     /**
909      * Returns the data network type
910      */
911     @Override
getVoiceNetworkType()912     public int getVoiceNetworkType() {
913         return mPhone.getServiceState().getVoiceNetworkType();
914     }
915 
916     /**
917      * @return true if a ICC card is present
918      */
hasIccCard()919     public boolean hasIccCard() {
920         return mPhone.getIccCard().hasIccCard();
921     }
922 
923     /**
924      * Return if the current radio is LTE on CDMA. This
925      * is a tri-state return value as for a period of time
926      * the mode may be unknown.
927      *
928      * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
929      * or {@link PHone#LTE_ON_CDMA_TRUE}
930      */
getLteOnCdmaMode()931     public int getLteOnCdmaMode() {
932         return mPhone.getLteOnCdmaMode();
933     }
934 
935     @Override
toggleHold()936     public void toggleHold() {
937         enforceModifyPermission();
938 
939         try {
940             PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
941         } catch (Exception e) {
942             Log.e(LOG_TAG, "Error during toggleHold().", e);
943         }
944     }
945 
946     @Override
merge()947     public void merge() {
948         enforceModifyPermission();
949 
950         try {
951             if (PhoneUtils.okToMergeCalls(mCM)) {
952                 PhoneUtils.mergeCalls(mCM);
953             }
954         } catch (Exception e) {
955             Log.e(LOG_TAG, "Error during merge().", e);
956         }
957     }
958 
959     @Override
swap()960     public void swap() {
961         enforceModifyPermission();
962 
963         try {
964             PhoneUtils.swap();
965         } catch (Exception e) {
966             Log.e(LOG_TAG, "Error during swap().", e);
967         }
968     }
969 
970     @Override
mute(boolean onOff)971     public void mute(boolean onOff) {
972         enforceModifyPermission();
973 
974         try {
975             PhoneUtils.setMute(onOff);
976         } catch (Exception e) {
977             Log.e(LOG_TAG, "Error during mute().", e);
978         }
979     }
980 
981     @Override
playDtmfTone(char digit, boolean timedShortTone)982     public void playDtmfTone(char digit, boolean timedShortTone) {
983         enforceModifyPermission();
984 
985         synchronized (mDtmfStopHandler) {
986             try {
987                 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
988             } catch (Exception e) {
989                 Log.e(LOG_TAG, "Error playing DTMF tone.", e);
990             }
991 
992             if (mDtmfStopRunnable != null) {
993                 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
994             }
995             mDtmfStopRunnable = new Runnable() {
996                 @Override
997                 public void run() {
998                     synchronized (mDtmfStopHandler) {
999                         if (mDtmfStopRunnable == this) {
1000                             mDtmfTonePlayer.stopDtmfTone();
1001                             mDtmfStopRunnable = null;
1002                         }
1003                     }
1004                 }
1005             };
1006             mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
1007         }
1008     }
1009 
1010     @Override
stopDtmfTone()1011     public void stopDtmfTone() {
1012         enforceModifyPermission();
1013 
1014         synchronized (mDtmfStopHandler) {
1015             try {
1016                 mDtmfTonePlayer.stopDtmfTone();
1017             } catch (Exception e) {
1018                 Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
1019             }
1020 
1021             if (mDtmfStopRunnable != null) {
1022                 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1023                 mDtmfStopRunnable = null;
1024             }
1025         }
1026     }
1027 
1028     @Override
addListener(ITelephonyListener listener)1029     public void addListener(ITelephonyListener listener) {
1030         enforcePrivilegedPhoneStatePermission();
1031 
1032         if (listener == null) {
1033             throw new IllegalArgumentException("Listener must not be null.");
1034         }
1035 
1036         synchronized (mListeners) {
1037             IBinder listenerBinder = listener.asBinder();
1038             for (ITelephonyListener l : mListeners) {
1039                 if (l.asBinder().equals(listenerBinder)) {
1040                     Log.w(LOG_TAG, "Listener already registered. Ignoring.");
1041                     return;
1042                 }
1043             }
1044             mListeners.add(listener);
1045             mDeathRecipients.put(listener.asBinder(),
1046                     new TelephonyListenerDeathRecipient(listener.asBinder()));
1047 
1048             // update the new listener so they get the full call state immediately
1049             for (Call call : mCallModeler.getFullList()) {
1050                 try {
1051                     notifyListenerOfCallLocked(call, listener);
1052                 } catch (RemoteException e) {
1053                     Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
1054                     removeListenerInternal(listener);
1055                 }
1056             }
1057         }
1058     }
1059 
1060     @Override
removeListener(ITelephonyListener listener)1061     public void removeListener(ITelephonyListener listener) {
1062         enforcePrivilegedPhoneStatePermission();
1063 
1064         if (listener == null) {
1065             throw new IllegalArgumentException("Listener must not be null.");
1066         }
1067 
1068         removeListenerInternal(listener);
1069     }
1070 
removeListenerInternal(ITelephonyListener listener)1071     private void removeListenerInternal(ITelephonyListener listener) {
1072         IBinder listenerBinder = listener.asBinder();
1073 
1074         synchronized (mListeners) {
1075             for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1076                 ITelephonyListener nextListener = it.next();
1077                 if (nextListener.asBinder().equals(listenerBinder)) {
1078                     TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
1079                     if (dr != null) {
1080                         dr.unlinkDeathRecipient();
1081                     }
1082                     it.remove();
1083                 }
1084             }
1085         }
1086     }
1087 
1088     /** CallModeler.Listener implementation **/
1089 
1090     @Override
onDisconnect(Call call)1091     public void onDisconnect(Call call) {
1092         notifyListenersOfCall(call);
1093     }
1094 
1095     @Override
onIncoming(Call call)1096     public void onIncoming(Call call) {
1097         notifyListenersOfCall(call);
1098     }
1099 
1100     @Override
onUpdate(List<Call> calls)1101     public void onUpdate(List<Call> calls) {
1102         for (Call call : calls) {
1103             notifyListenersOfCall(call);
1104         }
1105     }
1106 
1107     @Override
onPostDialAction( Connection.PostDialState state, int callId, String remainingChars, char c)1108     public void onPostDialAction(
1109             Connection.PostDialState state, int callId, String remainingChars, char c) { }
1110 
notifyListenersOfCall(Call call)1111     private void notifyListenersOfCall(Call call) {
1112         synchronized (mListeners) {
1113             for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1114                 ITelephonyListener listener = it.next();
1115                 try {
1116                     notifyListenerOfCallLocked(call, listener);
1117                 } catch (RemoteException e) {
1118                     TelephonyListenerDeathRecipient deathRecipient =
1119                             mDeathRecipients.get(listener.asBinder());
1120                     if (deathRecipient != null) {
1121                         deathRecipient.unlinkDeathRecipient();
1122                     }
1123                     it.remove();
1124                 }
1125             }
1126         }
1127     }
1128 
notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)1129     private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
1130             throws RemoteException {
1131         if (Binder.isProxy(listener)) {
1132             listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1133         } else {
1134             mMainThreadHandler.post(new Runnable() {
1135 
1136                 @Override
1137                 public void run() {
1138                     try {
1139                         listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1140                     } catch (RemoteException e) {
1141                         Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
1142                     }
1143                 }
1144             });
1145         }
1146 
1147     }
1148 
1149     private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
1150         private final IBinder mBinder;
1151 
TelephonyListenerDeathRecipient(IBinder listener)1152         public TelephonyListenerDeathRecipient(IBinder listener) {
1153             mBinder = listener;
1154             try {
1155                 mBinder.linkToDeath(this, 0);
1156             } catch (RemoteException e) {
1157                 unlinkDeathRecipient();
1158             }
1159         }
1160 
1161         @Override
binderDied()1162         public void binderDied() {
1163             synchronized (mListeners) {
1164                 if (mListeners.contains(mBinder)) {
1165                     mListeners.remove(mBinder);
1166                     Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
1167                 } else {
1168                     Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
1169                             "is not registered.");
1170                 }
1171             }
1172         }
1173 
unlinkDeathRecipient()1174         public void unlinkDeathRecipient() {
1175             mBinder.unlinkToDeath(this, 0);
1176         }
1177     }
1178 }
1179