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