I am developing an app in which we need to use the headphone jack as a button only.
Requirement : Play the default audio (calling) via earpiece when headsets are connected (no need of audio through headphones)
There are many example of routing audio through speaker and headphones and also bluetooth headsets but nothing about routing the audio through ear speakers of devices if headsets are connected.
I have tried a lot and some links are
Android : Force audio routing (not working in my scenario)
I have checked SoundAbout(https://play.google.com/store/apps/details?id=com.woodslink.android.wiredheadphoneroutingfix&hl=en)
app and it is routing the audio to various port like headset, speakers and earpieces.
I have got audio to speakers if headsets are connected:
Here is my code
if (Build.VERSION.SDK_INT >= 21) {
ForegroundService.audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
ForegroundService.audioManager.setSpeakerphoneOn(true);
SplashScreen.preferences.edit().putBoolean("isKey", true).commit();
} else {
Class audioSystemClass = null;
try {
audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
setForceUse.invoke(null, FOR_MEDIA, FORCE_SPEAKER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
SplashScreen.preferences.edit().putBoolean("isKey", true).commit();
ForegroundService.audioManager.setSpeakerphoneOn(true);
}
The earpiece is never used for media in Android, and it can only be used if the phone is in "call" or "communication" (VoIP) state.
I guess you have noticed that there is no "FORCE_EARPIECE" constant, so it can't be specified in a call to setForceUse.
Also, the earpiece has the lowest priority in output device selection for calls, so if the phone has anything connected to it (and in your case there is a fake headset), that device will be selected (see https://android.googlesource.com/platform/frameworks/av/+/322b4d2/services/audiopolicy/enginedefault/src/Engine.cpp#381).
Sorry, it doesn't seem to be possible to achieve what you intend.
UPDATE
After examining media.audio_policy state while SoundAbout is enforcing the use of earpiece for media, I have discovered the following tricks that this app uses:
It calls AudioSystem.setPhoneState(MODE_IN_COMMUNICATION) for enforcing "communication" phone state (usually used for VoIP calls).
If a headset (or headphones) is connected, in order to prevent the sound to be routed to it due to higher priority, the app calls AudioSystem.setDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET, DEVICE_STATE_UNAVAILABLE, ...) to trick Audio Manager to believe that there is no headset.
These are all hacks and require the app to monitor the phone state closely. It also doesn't work all the time.
Another drawback is that using earpiece disable on-chip audio decompression and thus has higher battery use.
In general, I wouldn't recommend using these techniques.
After researching a lot, I found it out that there is not any way to achieve this funtionality without using reflection.
First you need to put headset jack in and then call the method setWiredDeviceConnectionState() with suitable parameters then it behave like the headphone are disconnected but click works still.
So it is a hack but as per my requirement, it's not a foolproof solution but working for now.
Here is my code to do this,
private void sendIntent(Intent i) {
Method m;
Log.i(TAG, "Device sdk = " + Build.VERSION.SDK_INT);
try {
if (Build.VERSION.SDK_INT < 16) {
Class<?> clazz = Class.forName("android.app.ActivityManagerNative");
m = clazz.getMethod("broadcastStickyIntent", Intent.class, String.class);
m.setAccessible(true);
m.invoke(clazz, i, null);
return;
} else if (Build.VERSION.SDK_INT < 23) {
//int type, int state, String address, String name
m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class);
m.setAccessible(true);
Object[] objArr = new Object[3];
objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4;
objArr[1] = i.getIntExtra("state", 0);
objArr[2] = i.getStringExtra("name");
m.invoke(am, objArr);
} else {
//int type, int state, String address, String name
m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class, String.class);
m.setAccessible(true);
Object[] objArr = new Object[4];
objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4;
objArr[1] = i.getIntExtra("state", 0);
objArr[2] = i.getStringExtra("address");
objArr[3] = i.getStringExtra("name");
m.invoke(am, objArr);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
the intent to send :
#TargetApi(Build.VERSION_CODES.M)
public class HeadSetJackReciever extends AudioDeviceCallback {
public static boolean isAudioChecked;
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
if (addedDevices.length != 0) {
for (int i = 0; i < addedDevices.length; i++) {
if (addedDevices[i].getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
AudioDeviceInfo audioDeviceInfo = addedDevices[i];
int microphone = audioDeviceInfo.getType();
String headsetName = "DCS";
String headsetAddress = "";
try {
Method method = audioDeviceInfo.getClass().getMethod("getAddress");
method.setAccessible(true);
headsetAddress = (String) method.invoke(audioDeviceInfo);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Log.e("TEST", "microphone:"+microphone);
Log.e("TEST", "headsetName:"+headsetName);
Log.e("TEST", "headsetAddress:"+headsetAddress );
Intent intent = new Intent(ForegroundService.context, SelectAudioOutput.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("microphone",microphone);
intent.putExtra("headsetName",headsetName);
intent.putExtra("headsetAddress",headsetAddress);
ForegroundService.context.startActivity(intent);
}
}
}
}
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
if (removedDevices.length != 0) {
Log.e("TEST", "Audio deinserted");
if (SplashScreen.preferences.getBoolean("isKey", false)) {
Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION);
ForegroundService.context.startService(startIntent);
} else {
Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION);
ForegroundService.context.startService(startIntent);
}
ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL);
ForegroundService.audioManager.setSpeakerphoneOn(false);
}
}
}
for Lollipop and lower versions :
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
headsetName = intent.getStringExtra("name");
microphone = intent.getIntExtra("microphone", 0);
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
Log.d("onReceive", "Headset unplugged");
Log.e("TEST", "Audio deinserted");
if (SplashScreen.preferences.getBoolean("isKey", false)) {
Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION);
context.startService(startIntent);
} else {
Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION);
context.startService(startIntent);
}
ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL);
ForegroundService.audioManager.setSpeakerphoneOn(false);
break;
case 1:
Log.d("onReceive", "Headset plugged");
Log.e("TEST", "microphone:"+microphone);
Log.e("TEST", "headsetName:"+headsetName);
Log.e("TEST", "headsetAddress:"+headsetAddress );
Intent intentone = new Intent(ForegroundService.context, SelectAudioOutput.class);
intentone.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentone.putExtra("microphone",microphone);
intentone.putExtra("headsetName",headsetName);
intentone.putExtra("headsetAddress",headsetAddress);
context.startActivity(intentone);
break;
}
}
Let me know if I miss something.
Thanks.
Related
I want to control wifi hotspot dynamically in my Android app project. I have tired Reflection (which will not work in Android Oreo and later versions), startLocalOnyNetwork (but I want specific SSID and PASSWORD, which is not possible to configure it).
Then I rooted my phone, Is it possible if the device is rooted ?
Expecting an api to turn on/off wifi hotspot with specific SSID and PASSWORD or use the previous one.
Any possibilities or workarounds ?
Thanks in advance.
To turn on Wifi Hotspot, need some permissions
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
and the permission should be dynamically granted by user
In apps advanced settings -> Modify system settings
/**
* This enables tethering using the ssid/password defined in Settings App>Hotspot & tethering
* Does not require app to have system/privileged access
* Credit: Vishal Sharma - https://stackoverflow.com/a/52219887
*/
public boolean startTethering() {
File outputDir = mContext.getCodeCacheDir();
Object proxy;
try {
proxy = ProxyBuilder.forClass(OnStartTetheringCallbackClass())
.dexCache(outputDir).handler(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}).build();
} catch (Exception e) {
Log.e(TAG, "Error in enableTethering ProxyBuilder");
e.printStackTrace();
return false;
}
Method method = null;
try {
method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class);
if (method == null) {
Log.e(TAG, "startTetheringMethod is null");
} else {
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null);
Log.d(TAG, "startTethering invoked");
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error in enableTethering");
e.printStackTrace();
}
return false;
}
public void stopTethering() {
try {
Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class);
if (method == null) {
Log.e(TAG, "stopTetheringMethod is null");
} else {
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE);
Log.d(TAG, "stopTethering invoked");
}
} catch (Exception e) {
Log.e(TAG, "stopTethering error: " + e.toString());
e.printStackTrace();
}
}
Use above methods to turn on/off Wifi Hotspot with SSID and password defined in the settings.
private int AP_STATE_DISABLED = 11;
private int AP_STATE_ENABLING = 12;
private int AP_STATE_ENABLED = 13;
private int AP_STATE_ERROR = 14;
/**
* #return status hot spot enabled or not
*/
public boolean isHotSpotEnabled(Context context) {
Method method = null;
int actualState = 0;
try {
WifiManager mWifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
method = mWifiManager.getClass().getDeclaredMethod("getWifiApState");
method.setAccessible(true);
actualState = (Integer) method.invoke(mWifiManager, (Object[]) null);
if (actualState == AP_STATE_ENABLING ||actualState == AP_STATE_ENABLED) {
return true;
}
} catch (IllegalArgumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Above method can be used to get the current state of hotspot
I know how to turn on/off wifi hot spot using reflection in android using below method.
private static boolean changeWifiHotspotState(Context context,boolean enable) {
try {
WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Method method = manager.getClass().getDeclaredMethod("setWifiApEnabled", WifiConfiguration.class,
Boolean.TYPE);
method.setAccessible(true);
WifiConfiguration configuration = enable ? getWifiApConfiguration(manager) : null;
boolean isSuccess = (Boolean) method.invoke(manager, configuration, enable);
return isSuccess;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
But the above method is not working Android 8.0(Oreo).
When I execute above method in Android 8.0, I am getting below statement in logcat.
com.gck.dummy W/WifiManager: com.gck.dummy attempted call to setWifiApEnabled: enabled = true
Is there any other way to on/off hotspot on android 8.0
I thought the LocalOnlyHotspot route was the way to, but as #edsappfactory.com said in the comments - it only gives closed network, no internet access.
In Oreo hot-spotting/tethering moved to ConnectionManager, and its annotated #SystemApi, so (nominally) inaccessible.
As part of something else I was doing, I made an app and put it on github here. It uses reflection to get at the function and DexMaker to generate a subclass of ConnectionManager.OnStartTetheringCallback (which is also inaccessible).
Think it all works okay - bit rough around the edges, so please feel free to make better!
Relevant bits of code are in:
MyOreoWifiManager and;
CallbackMaker
I lost patience trying to get my DexMaker-generated callback to fire the MyOnStartTetheringCallback so all that code is in disarray and commented out.
Finally I got the solution.
Android 8.0, they provided public api to turn on/off hotspot. WifiManager
Below is the code to turn on hotspot
private WifiManager.LocalOnlyHotspotReservation mReservation;
#RequiresApi(api = Build.VERSION_CODES.O)
private void turnOnHotspot() {
WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
manager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
#Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
Log.d(TAG, "Wifi Hotspot is on now");
mReservation = reservation;
}
#Override
public void onStopped() {
super.onStopped();
Log.d(TAG, "onStopped: ");
}
#Override
public void onFailed(int reason) {
super.onFailed(reason);
Log.d(TAG, "onFailed: ");
}
}, new Handler());
}
private void turnOffHotspot() {
if (mReservation != null) {
mReservation.close();
}
}
onStarted(WifiManager.LocalOnlyHotspotReservation reservation) method will be called if hotspot is turned on.. Using WifiManager.LocalOnlyHotspotReservation reference you call close() method to turn off hotspot.
Note:
To turn on hotspot, the Location(GPS) should be enabled in the device. Otherwise, it will throw SecurityException
As per Jon suggestion, I got another way to enable WifiHotSpot in Android Oreo and above.
public boolean enableTetheringNew(MyTetheringCallback callback) {
File outputDir = mContext.getCodeCacheDir();
try {
proxy = ProxyBuilder.forClass(classOnStartTetheringCallback())
.dexCache(outputDir).handler(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "onTetheringStarted":
callback.onTetheringStarted();
break;
case "onTetheringFailed":
callback.onTetheringFailed();
break;
default:
ProxyBuilder.callSuper(proxy, method, args);
}
return null;
}
}).build();
} catch (IOException e) {
e.printStackTrace();
}
ConnectivityManager manager = (ConnectivityManager) mContext.getApplicationContext().getSystemService(ConnectivityManager.class);
Method method = null;
try {
method = manager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, classOnStartTetheringCallback(), Handler.class);
if (method == null) {
Log.e(TAG, "startTetheringMethod is null");
} else {
method.invoke(manager, TETHERING_WIFI, false, proxy, null);
}
return true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
private Class classOnStartTetheringCallback() {
try {
return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
Hi i am blinking the flashlight on incoming call and i am able to do that using this code but the problem is the flashlight is not getting stopped when the call is attended or cut
I am trying to stop the service from every way but the flashlight doesnot stops untill the loop is completed
In receiver class...
Intent in = new Intent(context, Run.class);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
in.putExtra("state", state);
context.startService(in);
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
// Toast.makeText(context, "Idle", Toast.LENGTH_LONG).show();
context.stopService(in);
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
// Toast.makeText(context, "Offhook", Toast.LENGTH_LONG).show();
context.stopService(in);
}
In Service class...
Camera cam;
Parameters p;
String state;
String tag="Runserve";
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
try {
cam = Camera.open();
p = cam.getParameters();
String myString = "0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101011";
long blinkDelay = 50;
for (int i = 0; i < myString.length(); i++) {
state=intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){
break;
}else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
break;
}
if (myString.charAt(i) == '0') {
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
cam.setParameters(p);
} else {
p.setFlashMode(Parameters.FLASH_MODE_OFF);
cam.setParameters(p);
}
Thread.sleep(blinkDelay);
}
}catch (Exception e) {
// TODO: handle exception
Log.d(tag, "in catch1");
Log.d(tag, e.toString());
}
}else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){
try {
p.setFlashMode(Parameters.FLASH_MODE_OFF);
cam.release();
} catch (Exception e) {
// TODO: handle exception
Log.d(tag, e.toString());
}
stopSelf();
}else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
try {
p.setFlashMode(Parameters.FLASH_MODE_OFF);
cam.release();
} catch (Exception e) {
// TODO: handle exception
Log.d(tag, e.toString());
}
stopSelf();
}
}
I am making the app in which i can attend the call through proximity sensor.This code has 2 intents First is i i.e working for versions less than Android 4.0 and another is headSetUnPluggedintent i.e working on Android 4.1. Is there any single way through which calls can be attended by all versions
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
TelephonyManager mgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
int callState = mgr.getCallState();
if(callState==TelephonyManager.CALL_STATE_RINGING) {
callState = mgr.getCallState();
String x="0.0";
String y=(String.valueOf(event.values[0]));
if( x.equals(y)){
//Toast.makeText(getApplicationContext(), "Proxy", Toast.LENGTH_SHORT).show();
try{
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT,new KeyEvent(KeyEvent.ACTION_UP,KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(i, "android.permission.CALL_PRIVILEGED");
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
sendOrderedBroadcast(headSetUnPluggedintent, null);
if(callState==TelephonyManager.CALL_STATE_OFFHOOK){
headSetUnPluggedintent=null;
i=null;
}else if(callState==TelephonyManager.CALL_STATE_IDLE){
headSetUnPluggedintent=null;
i=null;
}
}catch(Exception e){
Log.d(tag, e.toString());
}
}
Several points here :
in the for loop, it's useless to check the value of state as you have already checked it in your if condition. state is a String (i.e.immutable), it's value is not going to change.
you need to get, for each iteration, the current state of the call, using TelephonyManager#getCallState(). Something like this (not tested) :
TelephonyManager mgr = getSystemService(Context.TELEPHONY_SERVICE);
int callState = mgr.getCallState();
if (callState != TelephonyManager.CALL_STATE_RINGING )
break;
You should normally not attempt to stop a IntentService, it stops itself when all work is done (that is when onHandleIntent returns).
Also, the IntentService javadoc states that you should not call stopSelf().
I am writing an android app in which I need to answer an incoming call, do some work and then end the call.
After all the Googling I could find two different ways to achieve this both of which do not work with recent versions of Android, specifically after 4.1, Jelly Bean.
I.) Access "com.android.internal.telephony.ITelephony" using Java Reflection in the Broadcast receiver for "android.intent.action.PHONE_STATE". Below sample code can be found in hundreds of related post:
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
private ITelephony telephonyService;
#Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
Log.v(TAG, "Receving....");
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Log.v(TAG, "Get getTeleService...");
Class c = Class.forName(telephony.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
telephonyService = (ITelephony) m.invoke(telephony);
telephonyService.silenceRinger();
Log.v(TAG, "Answering Call now...");
telephonyService.answerRingingCall();
Log.v(TAG, "Call answered...");
//telephonyService.endCall();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG,
"FATAL ERROR: could not connect to telephony subsystem");
Log.e(TAG, "Exception object: " + e);
}
}
}
The problem with this code is that
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
is required for this method to work, and this permission has been defined as "for system apps only" from android v 2.3. In short, normal user apps can not define this permission in the manifest file anymore.
II.) Another way is to simulate pushing of the Headset hook which makes Android answer the call. This is done by broadcasting the "Intent.ACTION_MEDIA_BUTTON" as shown in below code.
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
#Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON);
answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(answer, null);
Log.d(TAG, "Answered incoming call from: " + number);
}
return;
}
}
This method works till Android 4.1 after which android has restricted user apps from broadcasting "Intent.ACTION_MEDIA_BUTTON".
So my conclusion is that currently there is no way how we can achieve this in Android 4.1 or later.
Has anybody else found any other solution or workaround to this problem?
This works from Android 2.2 to 4.0 and now after adding the try catch to the last line it works for 4.1.2 and 4.2 Frankly speaking dont know how it works but it works for me.
Log.d(tag, "InSecond Method Ans Call");
// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) {
e.printStackTrace();
}
This is working for me in Android 4.1.2 as well as i have tested on 4.2
This still gives an exception which is handled.
Edit for End Call
Hope this helps all the people looking for total solution for answer and end call.
/**
* Reject button click listener will reject the incoming call.
*/
private class RejectCallOnClickListener implements OnClickListener {
#Override
public void onClick(View v) {
Log.d(tag, "OnRejectButton: " + "Reject OnClick");
ignoreCall();
exitCleanly();
}
}
/**
* ignore incoming calls
*/
private void ignoreCall() {
if (USE_ITELEPHONY)
ignoreCallAidl();
else
ignoreCallPackageRestart();
}
/**
* AIDL/ITelephony technique for ignoring calls
*/
private void ignoreCallAidl() {
try {
// telephonyService.silenceRinger();
telephonyService.endCall();
} catch (RemoteException e) {
e.printStackTrace();
Log.d(tag, "ignoreCall: " + "Error: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
Log.d(tag, "ignoreCall" + "Error: " + e.getMessage());
}
}
/**
* package restart technique for ignoring calls
*/
private void ignoreCallPackageRestart() {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.restartPackage("com.android.providers.telephony");
am.restartPackage("com.android.phone");
}
/**
* cleanup and exit routine
*/
private void exitCleanly() {
unHookReceiver();
this.finish();
}
My application has been using the following code to answer the phone for about 6 months:
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(i, null);
I have tested this on Android Versions from 2.2 to 4.2.2. I have not seen a SecurityException broadcasting "Intent.ACTION_MEDIA_BUTTON" in my testing n a 4.2.2 device, nor have I seen crash reports from the Play Store indicating such exceptions are occurring.
I will say that this does not always work. It does not work on HTC devices due to the fact that HTC devices have a HeadsetObeserver that listen for the actually plugging in of a wired headset. Without this event, which it is currently a SecurityException for a third party app to broadcast, the HeadsetHook KeyEvent goes ignored.
The previous answers are misleading. The following code block does nothing:
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
except generate a SecurityException and catch it.
In the other answers where the code works, it is because of the KeyEvent.KEYCODE_HEADSETHOOK being broadcast.
Try this
Answer for end the call using pro grammatically. Its working fine for me.
try {
String serviceManagerName = "android.os.ServiceManager";
String serviceManagerNativeName = "android.os.ServiceManagerNative";
String telephonyName = "com.android.internal.telephony.ITelephony";
Class telephonyClass;
Class telephonyStubClass;
Class serviceManagerClass;
Class serviceManagerStubClass;
Class serviceManagerNativeClass;
Class serviceManagerNativeStubClass;
Method telephonyCall;
Method telephonyEndCall;
Method telephonyAnswerCall;
Method getDefault;
Method[] temps;
Constructor[] serviceManagerConstructor;
// Method getService;
Object telephonyObject;
Object serviceManagerObject;
telephonyClass = Class.forName(telephonyName);
telephonyStubClass = telephonyClass.getClasses()[0];
serviceManagerClass = Class.forName(serviceManagerName);
serviceManagerNativeClass = Class.forName(serviceManagerNativeName);
Method getService = // getDefaults[29];
serviceManagerClass.getMethod("getService", String.class);
Method tempInterfaceMethod = serviceManagerNativeClass.getMethod(
"asInterface", IBinder.class);
Binder tmpBinder = new Binder();
tmpBinder.attachInterface(null, "fake");
serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder);
IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone");
Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class);
telephonyObject = serviceMethod.invoke(null, retbinder);
//telephonyCall = telephonyClass.getMethod("call", String.class);
telephonyEndCall = telephonyClass.getMethod("endCall");
//telephonyAnswerCall = telephonyClass.getMethod("answerRingingCall");
telephonyEndCall.invoke(telephonyObject);
} catch (Exception e) {
e.printStackTrace();
Log.error(DialerActivity.this,
"FATAL ERROR: could not connect to telephony subsystem");
Log.error(DialerActivity.this, "Exception object: " + e);
}
As a conclusion to this thread, here is the code that works for me for Android 4.2.2.
--> Call is answered by simulating push of headset hook and keeping the broadcast in try-catch as mentioned by #PravinDodia in abouve thread. (Observe that an exception is thrown and handled in catch and the call is answered anyway. So I guess we can just ignore this exception and continue living life as if nothing happened! )
--> Call is disconnected using ITelephony.
public class PhoneCallReceiver extends BroadcastReceiver {
Context context = null;
private static final String TAG = "Phone call";
#Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals("android.intent.action.PHONE_STATE"))
return;
else {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
answerPhoneHeadsethook(context, intent);
return;
}
else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
Log.d(TAG, "CALL ANSWERED NOW!!");
try {
synchronized(this) {
Log.d(TAG, "Waiting for 10 sec ");
this.wait(10000);
}
}
catch(Exception e) {
Log.d(TAG, "Exception while waiting !!");
e.printStackTrace();
}
disconnectPhoneItelephony(context);
return;
}
else {
Log.d(TAG, "ALL DONE ...... !!");
}
}
}
public void answerPhoneHeadsethook(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d(TAG, "Incoming call from: " + number);
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
try {
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Log.d(TAG, "ACTION_MEDIA_BUTTON broadcasted...");
}
catch (Exception e) {
Log.d(TAG, "Catch block of ACTION_MEDIA_BUTTON broadcast !");
}
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone
headSetUnPluggedintent.putExtra("name", "Headset");
// TODO: Should we require a permission?
try {
context.sendOrderedBroadcast(headSetUnPluggedintent, null);
Log.d(TAG, "ACTION_HEADSET_PLUG broadcasted ...");
}
catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
Log.d(TAG, "Catch block of ACTION_HEADSET_PLUG broadcast");
Log.d(TAG, "Call Answered From Catch Block !!");
}
Log.d(TAG, "Answered incoming call from: " + number);
}
Log.d(TAG, "Call Answered using headsethook");
}
public static void disconnectPhoneItelephony(Context context) {
ITelephony telephonyService;
Log.v(TAG, "Now disconnecting using ITelephony....");
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Log.v(TAG, "Get getTeleService...");
Class c = Class.forName(telephony.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
telephonyService = (ITelephony) m.invoke(telephony);
//telephonyService.silenceRinger();
Log.v(TAG, "Disconnecting Call now...");
//telephonyService.answerRingingCall();
//telephonyService.endcall();
Log.v(TAG, "Call disconnected...");
telephonyService.endCall();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG,
"FATAL ERROR: could not connect to telephony subsystem");
Log.e(TAG, "Exception object: " + e);
}
}
}
At least the disconnect functionality works and we know how it works. So those who want to develop a Call Barring application can go ahead.
For those like me who want to answer a call, I guess we can use this for now and only hope that it does not stop working in the next version.
Try this :
Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");
// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Add permissions in AndroidManifest.xml file as
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
ITelephony method does not work on 4.4 and I find the headset/media button method still allows a fairly long ring before hangup.
This chaps blog post shows a new method that I have tested as working on 4.4.2 Galaxy s4 and HTC one mini that hangs up much more quickly and you don't get a missed call entry either.
http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-android-4.html
The technique uses a runtime exec as below, apparently you may need to use a different number for some devices.
public class HangupPhoneCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {
Executor eS = Executors.newSingleThreadExecutor();
eS.execute(new Runnable() {
#Override
public void run() {
Runtime runtime = Runtime.getRuntime();
try {
Log.d(TAG, "service call phone 5 \n");
runtime.exec("service call phone 5 \n");
} catch (Exception exc) {
Log.e(TAG, exc.getMessage());
}
}
});
return;
}
}
}
Call disconnecting using IT Telephony doesn't work on some devices like Samsung S Duos. But you can still make ringer silent :)
To end call in older version then 9.0
use this:
TelephonyManager tm = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);
Method m1 = null;
try {
m1 = tm.getClass().getDeclaredMethod("getITelephony");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
m1.setAccessible(true);
Object iTelephony = null;
try {
iTelephony = m1.invoke(tm);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Method m3 = null;
try {
m3 = iTelephony.getClass().getDeclaredMethod("endCall");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
m3.invoke(iTelephony);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
& for pie
TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
return telecomManager.endCall();
}
Make sure your compile SDK version is 28
private void PhoneControl(int nControl) {
if(nControl == PHONE_END_CALL) { // End call, all Android version
try {
TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
telecomManager.endCall();
}
}catch (Exception e) {}
try {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if(tm == null)
return;
tm.getClass().getMethod("endCall").invoke(tm);
bIsEnding = true;
} catch (Exception e) {
/* Do Nothing */ }
}
if(nControl == PHONE_ACCEPT_CALL) { // Accept phone call
// if(!bCallAccepted) { // Call déjà accepté => pas d'action (évite double action)
bCallAccepted = true;
if(Build.VERSION.SDK_INT >= 26) { // Pris en charge Android >= 8.0
if(context.checkSelfPermission("android.permission.ANSWER_PHONE_CALLS") == PackageManager.PERMISSION_GRANTED) {
TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
if(tm != null)
tm.acceptRingingCall();
}
}
if(Build.VERSION.SDK_INT >= 23 && Build.VERSION.SDK_INT < 26) { // Hangup in Android 6.x and 7.x
MediaSessionManager mediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
if(mediaSessionManager != null) {
try {
List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions
(new ComponentName(context, NotificationReceiverService.class));
for (MediaController m : mediaControllerList) {
if ("com.android.server.telecom".equals(m.getPackageName())) {
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
break;
}
}
} catch (Exception e) {
// Toast.makeText(instance, "sdfsdf123123"+e, Toast.LENGTH_LONG).show();
Intent answerCalintent = new Intent(context, AcceptCallActivity.class);
answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(answerCalintent);
/* Do Nothing */ }
}
// new Thread(new Runnable() {
//
// #Override
// public void run() {
// try {
// Runtime.getRuntime().exec("input keyevent " +
// Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
// } catch (IOException e) {
// // Runtime.exec(String) had an I/O problem, try to fall back
// String enforcedPerm = "android.permission.CALL_PRIVILEGED";
// Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
// Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
// KeyEvent.KEYCODE_HEADSETHOOK));
// Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
// Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
// KeyEvent.KEYCODE_HEADSETHOOK));
//
// context.sendOrderedBroadcast(btnDown, enforcedPerm);
// context.sendOrderedBroadcast(btnUp, enforcedPerm);
// }
// }
//
// }).start();
}
if(Build.VERSION.SDK_INT < 23) { // Prend en charge jusqu'à Android 5.1
try {
if(Build.MANUFACTURER.equalsIgnoreCase("HTC")) { // Uniquement pour HTC
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if(audioManager!=null && !audioManager.isWiredHeadsetOn()) {
Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
i.putExtra("state", 0);
i.putExtra("name", "Orasi");
try {
context.sendOrderedBroadcast(i, null);
} catch (Exception e) { /* Do Nothing */ }
}
}
Runtime.getRuntime().exec("input keyevent " +
Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
} catch (Exception e) {
// Runtime.exec(String) had an I/O problem, try to fall back
String enforcedPerm = "android.permission.CALL_PRIVILEGED";
Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_HEADSETHOOK));
Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(btnDown, enforcedPerm);
context.sendOrderedBroadcast(btnUp, enforcedPerm);
}
}
// }
}
}
<!--Incoming call state Listen-->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Store Data in Device/Shared pref-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--Get Incoming Number-->
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<!--Answer call-->
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission
android:name="android.permission.MODIFY_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<!--End Calll-->
<uses-permission android:name="android.permission.CALL_PHONE" />
public class NotificationReceiverService extends NotificationListenerService {
public NotificationReceiverService() {
}
}
<service
android:name=".notifications.NotificationReceiverService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
if(Build.VERSION.SDK_INT >= 26) { // Permission necessaire
if(checkSelfPermission("android.permission.ANSWER_PHONE_CALLS") != PackageManager.PERMISSION_GRANTED) {
String szPermissions[] = {"android.permission.ANSWER_PHONE_CALLS"};
requestPermissions(szPermissions, 0);
}
}
if(Build.VERSION.SDK_INT < 26 && Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1 )
{ // Permission pour Android 6.x et 7.x
ContentResolver contentResolver = getContentResolver();
String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = getPackageName();
if (enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName)) {
Intent intent2 = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
intent2 = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
}
startActivity(intent2);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
Intent intent=new Intent(MainActivity.this, Main2Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}, 1000);
}
}
I want to pair the device and connect it ,but I have a problem ,I just can pair device but I am not able to connect them. I want to know how to solve this problem. I'm afraid, I have not explained my problem very well, I can not connect means, connect your phone to a Bluetooth headset and I can only pair, here is the code
if (btAdapt.isEnabled()) {
tbtnSwitch.setChecked(false);
} else {
tbtnSwitch.setChecked(true);
}
// ============================================================
IntentFilter intent = new IntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(searchDevices, intent);
}
private BroadcastReceiver searchDevices = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
for (int i = 0; i < lstName.length; i++) {
String keyName = lstName[i].toString();
Log.e(keyName, String.valueOf(b.get(keyName)));
}
BluetoothDevice device = null;
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
String str = "no pair|" + device.getName() + "|"
+ device.getAddress();
if (lstDevices.indexOf(str) == -1)
lstDevices.add(str);
adtDevices.notifyDataSetChanged();
}
}else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING:
Log.d("BlueToothTestActivity", "it is pairing");
break;
case BluetoothDevice.BOND_BONDED:
Log.d("BlueToothTestActivity", "finish");
connect(device);
break;
case BluetoothDevice.BOND_NONE:
Log.d("BlueToothTestActivity", "cancel");
default:
break;
}
}
}
};
#Override
protected void onDestroy() {
this.unregisterReceiver(searchDevices);
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
class ItemClickEvent implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
if(btAdapt.isDiscovering())btAdapt.cancelDiscovery();
String str = lstDevices.get(arg2);
String[] values = str.split("\\|");
String address = values[2];
Log.e("address", values[2]);
BluetoothDevice btDev = btAdapt.getRemoteDevice(address);
try {
Boolean returnValue = false;
if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {
BluetoothDevice.createBond(BluetoothDevice remoteDevice);
Method createBondMethod = BluetoothDevice.class
.getMethod("createBond");
Log.d("BlueToothTestActivity", "start");
returnValue = (Boolean) createBondMethod.invoke(btDev);
}else if(btDev.getBondState() == BluetoothDevice.BOND_BONDED){
connect(btDev);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void connect(BluetoothDevice btDev) {
UUID uuid = UUID.fromString(SPP_UUID);
try {
btSocket = btDev.createInsecureRfcommSocketToServiceRecord(uuid);
Log.d("BlueToothTestActivity", "connecting...");
btSocket.connect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class ClickEvent implements View.OnClickListener {
public void onClick(View v) {
if (v == btnSearch)
{
if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {
Toast.makeText(BlueToothTestActivity.this, "please open", 1000)
.show();
return;
}
if (btAdapt.isDiscovering())
btAdapt.cancelDiscovery();
lstDevices.clear();
Object[] lstDevice = btAdapt.getBondedDevices().toArray();
for (int i = 0; i < lstDevice.length; i++) {
BluetoothDevice device = (BluetoothDevice) lstDevice[i];
String str = "pair|" + device.getName() + "|"
+ device.getAddress();
lstDevices.add(str);
adtDevices.notifyDataSetChanged();
}
setTitle("address:" + btAdapt.getAddress());
btAdapt.startDiscovery();
} else if (v == tbtnSwitch) {
if (tbtnSwitch.isChecked() == false)
btAdapt.enable();
else if (tbtnSwitch.isChecked() == true)
btAdapt.disable();
} else if (v == btnDis)
{
Intent discoverableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
} else if (v == btnExit) {
try {
if (btSocket != null)
btSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
BlueToothTestActivity.this.finish();
}
}
}
}
You have not specified, but I assume your problem is in the socket creation part rather than the actual connect() call. This is where things usually go wrong.
How to fix?
Your code assumes the headset supports insecure BT communications. In many cases this is true, but not in all. Try calling createRfcommSocketToServiceRecord() instead
Your code uses the default SPP UUID for the RFCOMM channel lookup. For API versions >= 15 this is the wrong thing to do. Instead try calling device.getUuids() and use the first returned UUID as the creation parameter.
From my own experience, even for pre-15 API versions you can still call getUuids() and get good results, but you will need to do it by reflection. Only if the above fails should you attempt socket creation with the default SPP UUID
If the above fails, you may try, as a last resort, to activate the "createRfcommSocket" hidden API . That had worked for me several occasions, and on multiple Android versions. Use java reflection to activate this call and, since inherently not safe, protect it with try catch.
Remember to place your logic in an AsyncTask or something. You do not want the UI thread to block on such tasks!
Finally feel free to use https://github.com/giladHaimov/BTWiz for a much simpler handling of the Bluetooth connect and for simple async IO interface.
Quoting your code:
btSocket = btDev.createInsecureRfcommSocketToServiceRecord(uuid);
Log.d("BlueToothTestActivity", "connecting...");
btSocket.connect();
You can find the code on the official Android documentation on connecting as a client, here.
I can see 4 things that might cause problems:
You should connect in a separate thread! .connect() is a blocking call - see link above
Not all devices accept insecure connections
For Android devices below 2.3.3 this method does not work. You have to call a private method via reflection - see this. Also I think you will find it on SO.
Surround .create.... with a try/catch and post errors on Logcat.
Can you post your Logcat logs?