FileDocCategorySizeDatePackage
PhoneInterfaceManager.javaAPI DocAndroid 1.5 API22138Wed May 06 22:42:46 BST 2009com.android.phone

PhoneInterfaceManager

public class PhoneInterfaceManager extends ITelephony.Stub
Implementation of the ITelephony interface.

Fields Summary
private static final String
LOG_TAG
private static final boolean
DBG
private static final int
CMD_HANDLE_PIN_MMI
private static final int
CMD_HANDLE_NEIGHBORING_CELL
private static final int
EVENT_NEIGHBORING_CELL_DONE
private static final int
CMD_ANSWER_RINGING_CALL
private static final int
CMD_END_CALL
private static final int
CMD_SILENCE_RINGER
PhoneApp
mApp
com.android.internal.telephony.Phone
mPhone
MainThreadHandler
mMainThreadHandler
Constructors Summary
public PhoneInterfaceManager(PhoneApp app, com.android.internal.telephony.Phone phone)

        mApp = app;
        mPhone = phone;
        mMainThreadHandler = new MainThreadHandler();
        publish();
    
Methods Summary
public voidanswerRingingCall()

        if (DBG) log("answerRingingCall...");
        // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
        // but that can probably wait till the big TelephonyManager API overhaul.
        // For now, protect this call with the MODIFY_PHONE_STATE permission.
        enforceModifyPermission();
        sendRequestAsync(CMD_ANSWER_RINGING_CALL);
    
private voidanswerRingingCallInternal()
Make the actual telephony calls to implement answerRingingCall(). This should only be called from the main thread of the Phone app.

see
answerRingingCall TODO: it would be nice to return true if we answered the call, or false if there wasn't actually a ringing incoming call, or some other error occurred. (In other words, pass back the return value from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().) But that would require calling this method via sendRequest() rather than sendRequestAsync(), and right now we don't actually *need* that return value, so let's just return void for now.

        final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
        if (hasRingingCall) {
            final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
            final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
            if (hasActiveCall && hasHoldingCall) {
                // Both lines are in use!
                // TODO: provide a flag to let the caller specify what
                // policy to use if both lines are in use.  (The current
                // behavior is hardwired to "answer incoming, end ongoing",
                // which is how the CALL button is specced to behave.)
                PhoneUtils.answerAndEndActive(mPhone);
                return;
            } else {
                // answerCall() will automatically hold the current active
                // call, if there is one.
                PhoneUtils.answerCall(mPhone);
                return;
            }
        } else {
            // No call was ringing.
            return;
        }
    
public voidcall(java.lang.String number)

        if (DBG) log("call: " + number);

        // This is just a wrapper around the ACTION_CALL intent, but we still
        // need to do a permission check since we're calling startActivity()
        // from the context of the phone app.
        enforceCallPermission();

        String url = createTelUrl(number);
        if (url == null) {
            return;
        }

        Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClassName(mApp, PhoneApp.getCallScreenClassName());
        mApp.startActivity(intent);
    
public voidcancelMissedCallsNotification()

        enforceModifyPermission();
        NotificationMgr.getDefault().cancelMissedCallNotification();
    
private java.lang.StringcreateTelUrl(java.lang.String number)

        if (TextUtils.isEmpty(number)) {
            return null;
        }

        StringBuilder buf = new StringBuilder("tel:");
        buf.append(number);
        return buf.toString();
    
public voiddial(java.lang.String number)

        if (DBG) log("dial: " + number);
        // No permission check needed here: This is just a wrapper around the
        // ACTION_DIAL intent, which is available to any app since it puts up
        // the UI before it does anything.

        String url = createTelUrl(number);
        if (url == null) {
            return;
        }

        // PENDING: should we just silently fail if phone is offhook or ringing?
        Phone.State state = mPhone.getState();
        if (state != Phone.State.OFFHOOK && state != Phone.State.RINGING) {
            Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mApp.startActivity(intent);
        }
    
public intdisableApnType(java.lang.String type)

        enforceModifyPermission();
        return mPhone.disableApnType(type);
    
public booleandisableDataConnectivity()

        enforceModifyPermission();
        return mPhone.disableDataConnectivity();
    
public voiddisableLocationUpdates()

        mApp.enforceCallingOrSelfPermission(
                android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
        mPhone.disableLocationUpdates();
    
public intenableApnType(java.lang.String type)

        enforceModifyPermission();
        return mPhone.enableApnType(type);
    
public booleanenableDataConnectivity()

        enforceModifyPermission();
        return mPhone.enableDataConnectivity();
    
public voidenableLocationUpdates()

        mApp.enforceCallingOrSelfPermission(
                android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
        mPhone.enableLocationUpdates();
    
public booleanendCall()

        enforceCallPermission();
        boolean hungUp = PhoneUtils.hangup(mPhone);
        if (DBG) log("endCall: " + (hungUp ? "hung up!" : "no call to hang up"));
        return hungUp;
    
private voidenforceCallPermission()
Make sure the caller has the CALL_PHONE permission.

throws
SecurityException if the caller does not have the required permission

        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
    
private voidenforceModifyPermission()
Make sure the caller has the MODIFY_PHONE_STATE permission.

throws
SecurityException if the caller does not have the required permission

        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
    
private voidenforceReadPermission()
Make sure the caller has the READ_PHONE_STATE permission.

throws
SecurityException if the caller does not have the required permission

        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
    
public intgetCallState()

        return DefaultPhoneNotifier.convertCallState(mPhone.getState());
    
public android.os.BundlegetCellLocation()

        try {
            mApp.enforceCallingOrSelfPermission(
                android.Manifest.permission.ACCESS_FINE_LOCATION, null);
        } catch (SecurityException e) {
            // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
            // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
            // is the weaker precondition
            mApp.enforceCallingOrSelfPermission(
                android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
        }
        Bundle data = new Bundle();
        mPhone.getCellLocation().fillInNotifierBundle(data);
        return data;
    
public intgetDataActivity()

        return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
    
public intgetDataState()

        return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
    
public java.util.ListgetNeighboringCellInfo()

        try {
            mApp.enforceCallingOrSelfPermission(
                    android.Manifest.permission.ACCESS_FINE_LOCATION, null);
        } catch (SecurityException e) {
            // If we have ACCESS_FINE_LOCATION permission, skip the check 
            // for ACCESS_COARSE_LOCATION 
            // A failure should throw the SecurityException from 
            // ACCESS_COARSE_LOCATION since this is the weaker precondition
            mApp.enforceCallingOrSelfPermission(
                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
        }            

        ArrayList<NeighboringCellInfo> cells = null;
    
        try {
            cells = (ArrayList<NeighboringCellInfo>) sendRequest(
                    CMD_HANDLE_NEIGHBORING_CELL, null);
        } catch (RuntimeException e) {
        }

        return (List <NeighboringCellInfo>) cells;
    
public booleanhandlePinMmi(java.lang.String dialString)

        enforceModifyPermission();
        return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
    
public booleanisDataConnectivityPossible()

        return mPhone.isDataConnectivityPossible();
    
public booleanisIdle()

        return (mPhone.getState() == Phone.State.IDLE);
    
public booleanisOffhook()

        return (mPhone.getState() == Phone.State.OFFHOOK);
    
public booleanisRadioOn()

        return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
    
public booleanisRinging()

        return (mPhone.getState() == Phone.State.RINGING);
    
public booleanisSimPinEnabled()

        enforceReadPermission();
        return (PhoneApp.getInstance().isSimPinEnabled());
    
private voidlog(java.lang.String msg)

        Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
    
private voidpublish()

        if (DBG) log("publish: " + this);

        ServiceManager.addService("phone", this);
    
private java.lang.ObjectsendRequest(int command, java.lang.Object argument)
Posts the specified command to be executed on the main thread, waits for the request to complete, and returns the result.

see
sendRequestAsync

        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
            throw new RuntimeException("This method will deadlock if called from the main thread.");
        }

        MainThreadRequest request = new MainThreadRequest(argument);
        Message msg = mMainThreadHandler.obtainMessage(command, request);
        msg.sendToTarget();

        // Wait for the request to complete
        synchronized (request) {
            while (request.result == null) {
                try {
                    request.wait();
                } catch (InterruptedException e) {
                    // Do nothing, go back and wait until the request is complete
                }
            }
        }
        return request.result;
    
private voidsendRequestAsync(int command)
Asynchronous ("fire and forget") version of sendRequest(): Posts the specified command to be executed on the main thread, and returns immediately.

see
sendRequest

        mMainThreadHandler.sendEmptyMessage(command);
    
public booleansetRadio(boolean turnOn)

        enforceModifyPermission();
        if ((mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF) != turnOn) {
            toggleRadioOnOff();
        }
        return true;
    
public booleanshowCallScreen()

        return showCallScreenInternal(false, false);
    
private booleanshowCallScreenInternal(boolean specifyInitialDialpadState, boolean initialDialpadState)

        if (isIdle()) {
            return false;
        }
        // If the phone isn't idle then go to the in-call screen
        long callingId = Binder.clearCallingIdentity();
        try {
            Intent intent;
            if (specifyInitialDialpadState) {
                intent = PhoneApp.createInCallIntent(initialDialpadState);
            } else {
                intent = PhoneApp.createInCallIntent();
            }
            mApp.startActivity(intent);
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
        return true;
    
public booleanshowCallScreenWithDialpad(boolean showDialpad)

        return showCallScreenInternal(true, showDialpad);
    
public voidsilenceRinger()

        if (DBG) log("silenceRinger...");
        // TODO: find a more appropriate permission to check here.
        // (That can probably wait till the big TelephonyManager API overhaul.
        // For now, protect this call with the MODIFY_PHONE_STATE permission.)
        enforceModifyPermission();
        sendRequestAsync(CMD_SILENCE_RINGER);
    
private voidsilenceRingerInternal()
Internal implemenation of silenceRinger(). This should only be called from the main thread of the Phone app.

see
silenceRinger

        if ((mPhone.getState() == Phone.State.RINGING)
            && mApp.notifier.isRinging()) {
            // Ringer is actually playing, so silence it.
            if (DBG) log("silenceRingerInternal: silencing...");
            PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE);
            mApp.notifier.silenceRinger();
        }
    
public booleansupplyPin(java.lang.String pin)

        enforceModifyPermission();
        final CheckSimPin checkSimPin = new CheckSimPin(mPhone.getSimCard());
        checkSimPin.start();
        return checkSimPin.checkPin(pin);
    
public voidtoggleRadioOnOff()

        enforceModifyPermission();
        mPhone.setRadioPower(!isRadioOn());
    
public voidupdateServiceLocation()

        // No permission check needed here: this call is harmless, and it's
        // needed for the ServiceState.requestStateUpdate() call (which is
        // already intentionally exposed to 3rd parties.)
        mPhone.updateServiceLocation(null);