zoukankan      html  css  js  c++  java
  • start and end call use itelephony and how to pick up a call

    Bluetooth Headset service
     
     但想想而已。 没有蓝牙耳机如何调用它来接听电话。想想有点搞笑。

    网上扒的通过添加一个ITelephony.aidl 来反射,注意aidl的写法,如果使用studio 来写,最好是利用菜单生成aidl或者抄写下它是如何生成的。

    这里解释了一下原理;

    使用java 反射来获取安卓内部的私有方法

    TelephonyManager 类是由远程服务来实现的,它实质是

    调用远程电话服务,这个工作具体是由AIDL来做的,remote procedure call  (RPC) 

    这样远程服务使用公有TelephonyManager 就可以不暴露实现,你需要做的就是利用 getITelephony() 来得到远程程序调用的客户端,此方法返回一个ITelephony类型的对象。

    有了这个对象,就可以得到其类对象和内部方法endCall() ,这样我们就能调用这个方法。

    现在这个endCall() 是运行在远程程序调用的客户端,它可以发送消息给 远程电话服务(运行在远程服务中),要求终止当前通话。

    由于源代码 ITelephony.aidl 是公开的,你可以将代码放在你的工程中,IDE会自动生成ITelephony.java(自动包含了RPC的客户端)

    当你在安卓设备上运行时,会调用安卓框架里的ITelephony 对象,并将其转换成 com.android.internal.telephony.ITelephony

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

    附上实现:

    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

  • 相关阅读:
    英语词汇辨异 —— 形近字、近义词
    英文构词法 —— circum- 前缀
    英文构词法 —— circum- 前缀
    MySQL Cluster-备份恢复初步测试
    MySQL root密码重置报错:mysqladmin: connect to server at 'localhost' failed的解决方案!
    [置顶] High Performance Canvas Game for Android
    [移动网关]2G环境下资源下载有一定概率失败,客户端日志显示收到403错误
    工作两年,新起点,新征程
    CloudStack 物理网络架构
    数学之路(3)-机器学习(3)-机器学习算法-欧氏距离(1)
  • 原文地址:https://www.cnblogs.com/iamgoodman/p/4655339.html
Copyright © 2011-2022 走看看