start and end call use itelephony and how to pick up a call

Bluetooth Headset service
 
 但想一想而已。 沒有藍牙耳機如何調用它來接聽電話。想一想有點搞笑。

 

 

 

 

 

網上扒的經過添加一個ITelephony.aidl 來反射,注意aidl的寫法,若是使用studio 來寫,最好是利用菜單生成aidl或者抄寫下它是如何生成的。java

這裏解釋了一下原理;android

使用java 反射來獲取安卓內部的私有方法web

TelephonyManager 類是由遠程服務來實現的,它實質是app

調用遠程電話服務,這個工做具體是由AIDL來作的,remote procedure call  (RPC) 框架

這樣遠程服務使用公有TelephonyManager 就能夠不暴露實現,你須要作的就是利用 getITelephony() 來獲得遠程程序調用的客戶端,此方法返回一個ITelephony類型的對象。ide

有了這個對象,就能夠獲得其類對象和內部方法endCall() ,這樣咱們就能調用這個方法。wordpress

如今這個endCall() 是運行在遠程程序調用的客戶端,它能夠發送消息給 遠程電話服務(運行在遠程服務中),要求終止當前通話。post

因爲源代碼 ITelephony.aidl 是公開的,你能夠將代碼放在你的工程中,IDE會自動生成ITelephony.java(自動包含了RPC的客戶端)測試

當你在安卓設備上運行時,會調用安卓框架裏的ITelephony 對象,並將其轉換成 com.android.internal.telephony.ITelephony ui

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

附上實現:

AndroidManifest.xml 

 

...
    <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="ANDROID.PERMISSION.MODIFY_PHONE_STATE"/> ...

 

FragmentCallPhone.java

 

package org.nd.ui;

import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

import org.nd.R;

import java.lang.reflect.Method;


import org.nd.common.Common;


/**
 * Created by HAO on 2015/7/17.
 */
public class FragmentCallPhone extends Fragment implements View.OnClickListener {

    public FragmentCallPhone() {
    }

    private Button btnCall, btnEnd;
    private TextView edit_phone;
    private static final int AUTO_END_CALL_AFTER_ONE_MINUTE = 10000;
    private Handler mHandler;
    private Handler cutHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Common.HEART_BEAT:
                    Log.d("org.nd", "HEART_BEAT_TWO");
                    break;
                case Common.REFLECTION_ERROR:
                    Log.d("org.nd", "HEART_BEAT_THREE");
                    break;
                case Common.HEART_BEAT_TWO:
                    Log.d("org.nd", "HEART_BEAT_ONE ");
                    break;
            }
        }
    };
    private boolean call_once = true;

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_layout_call, container, false);
        btnCall = (Button) view.findViewById(R.id.call_phone);
        btnCall.setOnClickListener(this);
        btnEnd = (Button) view.findViewById(R.id.dial_and_call);
        btnEnd.setOnClickListener(this);
        edit_phone = (TextView) view.findViewById(R.id.edit_phone);

        MyPhoneListener phoneListener = new MyPhoneListener();
        TelephonyManager telephonyManager =

                (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
        telephonyManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
        mHandler = new Handler();
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
       // btnCall.callOnClick();
    }


    @Override
    public void onClick(View v) {
        String uri, number = edit_phone.getText().toString();
        switch (v.getId()) {
            case R.id.call_phone:
                if (number.isEmpty())
                    number = "10000";
                //   if(!autoEnd.isChecked())
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        call_once = false;
                        TelephonyManager telephonyManager = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);

                        Message msg = new Message();
                        try {
                            Class c = Class.forName(telephonyManager.getClass().getName());
                            Method m = c.getDeclaredMethod("getITelephony");
                            m.setAccessible(true);
                            Object telephonyService = m.invoke(telephonyManager); // Get the internal ITelephony object
                            c = Class.forName(telephonyService.getClass().getName()); // Get its class
                            m = c.getDeclaredMethod("endCall"); // Get the "endCall()" method
                            m.setAccessible(true); // Make it accessible
                            m.invoke(telephonyService); // invoke endCall()
                            msg.what = Common.HEART_BEAT;
                            cutHandler.sendMessage(msg);
                        } catch (Exception e) {
                            msg = new Message();
                            msg.what = Common.REFLECTION_ERROR;
                            cutHandler.sendMessage(msg);
                            Log.d("org.nd", e.toString());
                        }
                    }
                }, AUTO_END_CALL_AFTER_ONE_MINUTE);

                uri = "tel:" + number;
                Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(uri));
                startActivity(callIntent);
                Message msg = new Message();
                msg.what = Common.HEART_BEAT_TWO;
                cutHandler.sendMessage(msg);
                break;
            case R.id.dial_and_call:
                uri = "tel:" + edit_phone.getText().toString();
                Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(uri));
                startActivity(dialIntent);
                break;

        }
    }

    private class MyPhoneListener extends PhoneStateListener {
        private boolean onCall = false;

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    // phone ringing...
                    Toast.makeText(getActivity(), incomingNumber + " calls you",
                            Toast.LENGTH_LONG).show();
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    // one call exists that is dialing, active, or on hold
                    Toast.makeText(getActivity(), "on call...",
                            Toast.LENGTH_LONG).show();
                    //because user answers the incoming call
                    onCall = true;
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    // in initialization of the class and at the end of phone call
                    // detect flag from CALL_STATE_OFFHOOK
                    if (onCall == true) {
                        Toast.makeText(getActivity(), "restart app after call",
                                Toast.LENGTH_LONG).show();
                        // restart our application
                        Intent restart = getActivity().getBaseContext().getPackageManager().
                                getLaunchIntentForPackage(getActivity().getBaseContext().getPackageName());
                        restart.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(restart);
                        onCall = false;
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

 因爲接電話須要改變電話的狀態,而谷歌對於這一行爲後來都加了保護,不容許開發者修改。因此引起了無數開發者的研究熱情。

源頭是這樣的:

public static final String MODIFY_PHONE_STATE

Added in API level 1
Allows modification of the telephony state - power on, mmi, etc. Does not include placing calls.

Not for use by third-party applications.

Constant Value: "android.permission.MODIFY_PHONE_STATE"


方法一: answerRingingCall

這是按照掛電話的代碼寫的。不經過;

                TelephonyManager telephonyManager = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);  
                try {
                    Class c = Class.forName(telephonyManager.getClass().getName());
                    Method m = c.getDeclaredMethod("getITelephony");
                    m.setAccessible(true);
                    Object telephonyService = m.invoke(telephonyManager); // Get the internal ITelephony object 
                    c = Class.forName(telephonyService.getClass().getName()); // Get its class 
                    m = c.getDeclaredMethod("answerRingingCall"); // Get the method
                    m.setAccessible(true); // Make it accessible
                    m.invoke(telephonyService); // invoke
                    Log.d(FRAGMENT_TAG, "success?");
                } catch (Exception e) {
                    Log.d(FRAGMENT_TAG, getErrorInfoFromException(e));
                }

 

java.lang.SecurityException: Neither user 10172 nor current process has android.permission.MODIFY_PHONE_STATE.


方法二:見這裏。

適用於Android2.3及2.3以上的版本上 ,但測試發現4.1系統上無論用。
            // 報錯: java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.

 

可是方法二能夠改進:

 if (android.os.Build.VERSION.SDK_INT >= 15) {
            Intent meidaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
            KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK);
            meidaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
            getActivity().sendOrderedBroadcast(meidaButtonIntent, null);
        }

而後就行了,如今我須要驗證一下unroot手機是否經過。

 

過了幾天發現,在5.0(Lollipo )平臺上又是無做爲的。敢情真是逃不掉Root

相關文章
相關標籤/搜索