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