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