To discover the list of services using NSDManager class I had implemented some code with google reference but I cannot get the host address when I try to discover the services. It is showing null when Discovery started. I had referred this link for discovering the list of devices connected in the same network i followed this blog to get all the services when connected to same network.
Is my approach is correct to get the list of services/devices connected to same network?
I tried following code to discover list
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=(TextView)findViewById(R.id.mytext);
mylist=new List<NsdManager.DiscoveryListener>() {
#Override
public int size() {
return s;
}
#Override
public boolean add(NsdManager.DiscoveryListener discoveryListener) {
return true;
}
#Override
public NsdManager.DiscoveryListener get(int i) {
return mDiscoveryListener;
}
#Override
public NsdManager.DiscoveryListener set(int i, NsdManager.DiscoveryListener discoveryListener) {
return mDiscoveryListener;
}
#Override
public NsdManager.DiscoveryListener remove(int i) {
return mDiscoveryListener;
}
};
nsdList=new ArrayList<>();
nsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
//broadcaster = LocalBroadcastManager.getInstance(mContext);
registerService(port);
initializeResolveListener();
discoverServices();
}
public void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() {
#Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
#Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service discovery success : " + service);
String serviceType = service.getServiceType();
mylist.add(mDiscoveryListener);
nsdList.add(service.getServiceName());
s++;
Log.d(TAG, "Service discovery success: " + service.getServiceName());
boolean isOurService = serviceType.equals(SERVICE_TYPE) || serviceType.equals(SERVICE_TYPE);
if (!isOurService) {
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(SERVICE_NAME)) {
Log.d(TAG, "Same machine: " + SERVICE_NAME);
} else if (service.getServiceName().contains(SERVICE_NAME)) {
Log.d(TAG, "different machines. (" + service.getServiceName() + "-" +
SERVICE_NAME+ ")");
nsdManager.resolveService(service, mResolveListener);
}
}
Registered service with
public void registerService(int port) {
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName(SERVICE_NAME);
serviceInfo.setServiceType(SERVICE_TYPE);
serviceInfo.setPort(port);
nsdManager.registerService(serviceInfo,
NsdManager.PROTOCOL_DNS_SD,
registrationListener);
}
my resolve listener
public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() {
#Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "Resolve failed" + errorCode);
}
#Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.v(TAG, "Resolve Succeeded. " + serviceInfo);
if (serviceInfo.getServiceName().equals(mService)) {
Log.d(TAG, "Same IP.");
return;
}
mService = serviceInfo;
int port = mService.getPort();
InetAddress host = mService.getHost(); }
};
}
When I try to execute the code it shows null
When you discover a service, it's just a PTR record:
_http._tcp.local. type PTR, class IN, myService._http._tcp.local.
If you want the host/port info, you must resolve that record even it's advertised by yourself.
Related
Hello I am new to Android Development. I have created an NSD Utility class. Currently its unable to call the method DiscoveryListner. Any help will be great
MainActivity
package com.example.android.implicitintents;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.android.implicitintents.Utils.NsdUtils;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "NsdChat";
public static String URL_WEB;
NsdUtils mNsdUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NsdUtils mNsdUtils = new NsdUtils(this);
mNsdUtils.initializeNsd();
}
public void onClickOpenWebpageButton(View view) {
String urlAsString = "http://192.168.10.1";
openWebPage(urlAsString);
}
private void openWebPage(String url) {
Intent intent = new Intent(this, ConfigActivity.class);
intent.putExtra(URL_WEB, url);
Log.d(TAG, "Button Pressed");
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
}
NSD Utility Class
package com.example.android.implicitintents.Utils;
import android.content.Context;
import android.net.nsd.NsdServiceInfo;
import android.net.nsd.NsdManager;
import android.util.Log;
public class NsdUtils {
Context mContext;
NsdManager mNsdManager;
NsdManager.ResolveListener mResolveListener;
NsdManager.DiscoveryListener mDiscoveryListener;
public static final String SERVICE_TYPE = "http";
public static final String TAG = "NsdHelper";
public String mServiceName = "Plug_Service";
NsdServiceInfo mService;
public NsdUtils(Context context) {
mContext = context;
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
}
public void initializeNsd() {
initializeResolveListener();
initializeDiscoveryListener();
}
public void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() {
#Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
#Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service discovery success" + service);
if (!service.getServiceType().equals(SERVICE_TYPE)) {
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same machine: " + mServiceName);
} else if (service.getServiceName().contains(mServiceName)){
mNsdManager.resolveService(service, mResolveListener);
}
}
#Override
public void onServiceLost(NsdServiceInfo service) {
Log.e(TAG, "service lost" + service);
if (mService == service) {
mService = null;
}
}
#Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
}
#Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
#Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
};
}
public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() {
#Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "Resolve failed" + errorCode);
}
#Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
if (serviceInfo.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same IP.");
return;
}
mService = serviceInfo;
}
};
}
public void discoverServices() {
mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
}
I tried calling the dicoverService in onclick button event in failed. Do we need to create a Async Task or a new Thread?
first specify the type of TCP network you want to find. Then make the object of the class and call the Intilization and then the discovery function of the NSD.
It should work just fine.
It is not necessary to run this functionality in another thread.
After some investigation current functionality stop crash and start discovery, when change String SERVICE_TYPE = "http"; to public static final String SERVICE_TYPE = "_name._tcp";
You can discover tcp service and then when you will obtain IP just connect to web server via http use those IP adress.
I am creating the voice recognition service like "Ok Google" with custom command. For this I have used the VoiceInteractionService and I refered the code from here. Also, I am getting the STATE_HARDWARE_UNAVAILABLE error in onAvailabilityChanged.
public class VoiceCommandService extends VoiceInteractionService {
private static final String TAG = "AlwaysOnHotwordDetector";
Locale locale = new Locale("en-US");
protected SpeechRecognizer mSpeechRecognizer;
protected Intent mSpeechRecognizerIntent;
public final Callback mHotwordCallback = new Callback() {
#Override
public void onAvailabilityChanged(int status) {
Log.i(TAG, "onAvailabilityChanged(" + status + ")");
hotwordAvailabilityChangeHelper(status);
}
#Override
public void onDetected(EventPayload eventPayload) {
Log.i(TAG, "onDetected");
}
#Override
public void onError() {
Log.i(TAG, "onError");
}
#Override
public void onRecognitionPaused() {
Log.i(TAG, "onRecognitionPaused");
}
#Override
public void onRecognitionResumed() {
Log.i(TAG, "onRecognitionResumed");
}
};
private AlwaysOnHotwordDetector mHotwordDetector;
#Override
public void onCreate(){
Log.d(TAG, "Entered on create");
super.onCreate();
}
#Override
public void onReady() {
super.onReady();
Log.i(TAG, "Creating " + this);
mHotwordDetector = createAlwaysOnHotwordDetector(
"Hello", Locale.forLanguageTag("en-US"), mHotwordCallback);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle args = new Bundle();
args.putParcelable("intent", new Intent(this, MainActivity.class));
showSession(args, 0);
stopSelf(startId);
return START_NOT_STICKY;
}
private void hotwordAvailabilityChangeHelper(int availability) {
Log.i(TAG, "Hotword availability = " + availability);
switch (availability) {
case AlwaysOnHotwordDetector.STATE_HARDWARE_UNAVAILABLE:
Log.i(TAG, "STATE_HARDWARE_UNAVAILABLE");
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNSUPPORTED:
Log.i(TAG, "STATE_KEYPHRASE_UNSUPPORTED");
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
Intent enroll = mHotwordDetector.createEnrollIntent();
Log.i(TAG, "Need to enroll with " + enroll);
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_ENROLLED - starting recognition");
if (mHotwordDetector.startRecognition(0)) {
Log.i(TAG, "startRecognition succeeded");
} else {
Log.i(TAG, "startRecognition failed");
}
break;
}
}}
VoiceInteractionService need android.permission.BIND_VOICE_INTERACTION in AndroidManifests
I try to implement a small test application for Googles Nearby Connections API. Unfortunately on 2 of 3 tested devices Google Play Services chrash when discovering or advertising. (OnePlus One, Android 6.1; Acer Iconia, Android 4.4)
I see other devices, but when i connect to one of them play service crash (only my Honor 8 keeps on working). It says the connection is suspendend with error code 1. According to Google this means "A suspension cause informing that the service has been killed."
Maybe some of you could help. I made this code based on this tutorial.
Code without imports:
MainActivity.java
package com.example.steffen.nearbyconnectionsdemo;
public class MainActivity extends Activity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
View.OnClickListener,
Connections.ConnectionRequestListener,
Connections.MessageListener,
Connections.EndpointDiscoveryListener {
// Identify if the device is the host
private boolean mIsHost = false;
GoogleApiClient mGoogleApiClient = null;
Button bt_ad, bt_search, bt_send;
TextView tv_status;
CheckBox checkBox;
Context c;
String globalRemoteEndpointId = "";
EditText editText;
final int MY_PERMISSIONS_REQUEST = 666;
private static int[] NETWORK_TYPES = {ConnectivityManager.TYPE_WIFI,
ConnectivityManager.TYPE_ETHERNET};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
c = this;
checkPermisson();
editText = (EditText) findViewById(R.id.editText);
bt_ad = (Button) findViewById(R.id.bt_ad);
bt_ad.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
bt_search.setEnabled(false);
startAdvertising();
}
});
bt_search = (Button) findViewById(R.id.bt_search);
bt_search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startDiscovery();
}
});
bt_send = (Button) findViewById(R.id.bt_send);
bt_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String message = "message: " + editText.getText().toString();
Toast.makeText(c, "Sending: " + message, Toast.LENGTH_SHORT).show();
byte[] payload = message.getBytes();
Nearby.Connections.sendReliableMessage(mGoogleApiClient, globalRemoteEndpointId, payload);
}
});
tv_status = (TextView) findViewById(R.id.tv_status);
checkBox = (CheckBox) findViewById(R.id.checkBox);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Nearby.CONNECTIONS_API)
.build();
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
private boolean isConnectedToNetwork() {
ConnectivityManager connManager =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
for (int networkType : NETWORK_TYPES) {
NetworkInfo info = connManager.getNetworkInfo(networkType);
if (info != null && info.isConnectedOrConnecting()) {
return true;
}
}
return false;
}
private void startAdvertising() {
if (!isConnectedToNetwork()) {
// Implement logic when device is not connected to a network
tv_status.setText("No Network");
return;
}
// Identify that this device is the host
mIsHost = true;
checkBox.setChecked(mIsHost);
// Advertising with an AppIdentifer lets other devices on the
// network discover this application and prompt the user to
// install the application.
List<AppIdentifier> appIdentifierList = new ArrayList<>();
appIdentifierList.add(new AppIdentifier(getPackageName()));
AppMetadata appMetadata = new AppMetadata(appIdentifierList);
// The advertising timeout is set to run indefinitely
// Positive values represent timeout in milliseconds
long NO_TIMEOUT = 0L;
String name = null;
Nearby.Connections.startAdvertising(mGoogleApiClient, name, appMetadata, NO_TIMEOUT,
this).setResultCallback(new ResultCallback<Connections.StartAdvertisingResult>() {
#Override
public void onResult(Connections.StartAdvertisingResult result) {
if (result.getStatus().isSuccess()) {
// Device is advertising
tv_status.setText("Advertising");
} else {
int statusCode = result.getStatus().getStatusCode();
// Advertising failed - see statusCode for more details
tv_status.setText("Error: " + statusCode);
}
}
});
}
private void startDiscovery() {
if (!isConnectedToNetwork()) {
// Implement logic when device is not connected to a network
tv_status.setText("No Network");
return;
}
String serviceId = getString(R.string.service_id);
// Set an appropriate timeout length in milliseconds
long DISCOVER_TIMEOUT = 1000L;
// Discover nearby apps that are advertising with the required service ID.
Nearby.Connections.startDiscovery(mGoogleApiClient, serviceId, DISCOVER_TIMEOUT, this)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
// Device is discovering
tv_status.setText("Discovering");
} else {
int statusCode = status.getStatusCode();
// Advertising failed - see statusCode for more details
tv_status.setText("Error: " + statusCode);
}
}
});
}
#Override
public void onEndpointFound(final String endpointId, String deviceId,
String serviceId, final String endpointName) {
// This device is discovering endpoints and has located an advertiser.
// Write your logic to initiate a connection with the device at
// the endpoint ID
Toast.makeText(this, "Found Device: " + serviceId + ", " + endpointName + ". Start Connection Try", Toast.LENGTH_SHORT).show();
connectTo(endpointId, endpointName);
}
private void connectTo(String remoteEndpointId, final String endpointName) {
// Send a connection request to a remote endpoint. By passing 'null' for
// the name, the Nearby Connections API will construct a default name
// based on device model such as 'LGE Nexus 5'.
tv_status.setText("Connecting");
String myName = null;
byte[] myPayload = null;
Nearby.Connections.sendConnectionRequest(mGoogleApiClient, myName,
remoteEndpointId, myPayload, new Connections.ConnectionResponseCallback() {
#Override
public void onConnectionResponse(String remoteEndpointId, Status status,
byte[] bytes) {
if (status.isSuccess()) {
// Successful connection
tv_status.setText("Connected to " + endpointName);
globalRemoteEndpointId = remoteEndpointId;
} else {
// Failed connection
tv_status.setText("Connecting failed");
}
}
}, this);
}
#Override
public void onConnectionRequest(final String remoteEndpointId, String remoteDeviceId,
final String remoteEndpointName, byte[] payload) {
if (mIsHost) {
byte[] myPayload = null;
// Automatically accept all requests
Nearby.Connections.acceptConnectionRequest(mGoogleApiClient, remoteEndpointId,
myPayload, this).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
String statusS = "Connected to " + remoteEndpointName;
Toast.makeText(c, statusS,
Toast.LENGTH_SHORT).show();
tv_status.setText(statusS);
globalRemoteEndpointId = remoteEndpointId;
} else {
String statusS = "Failed to connect to: " + remoteEndpointName;
Toast.makeText(c, statusS,
Toast.LENGTH_SHORT).show();
tv_status.setText(statusS);
}
}
});
} else {
// Clients should not be advertising and will reject all connection requests.
Nearby.Connections.rejectConnectionRequest(mGoogleApiClient, remoteEndpointId);
}
}
#Override
public void onMessageReceived(String endpointId, byte[] payload, boolean b) {
String message = payload.toString();
Toast.makeText(this, "Received from " + endpointId + ": " + message, Toast.LENGTH_SHORT).show();
}
#Override
public void onClick(View view) {
}
#Override
public void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
public void onStop() {
super.onStop();
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
tv_status.setText("Connection suspended because of " + i);
mGoogleApiClient.reconnect();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
tv_status.setText("Connection Failed");
}
#Override
public void onEndpointLost(String s) {
tv_status.setText("Endpoint lost: " + s);
}
#Override
public void onDisconnected(String s) {
tv_status.setText("Disconnected: " + s);
}
public void checkPermisson(){
Toast.makeText(c, "Check permission", Toast.LENGTH_SHORT).show();
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_NETWORK_STATE}, MY_PERMISSIONS_REQUEST);
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
Toast.makeText(c, "Permission granted", Toast.LENGTH_SHORT).show();
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(c, "Permission not granted, app may fail", Toast.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
I currently have a method which writes to the BLE devices to beep it. My Bluetooth Callback goes as follows :
ReadCharacteristic rc = new ReadCharacteristic(context, ds.getMacAddress(), serviceUUID, UUID.fromString(myUUID), "") {
#Override
public void onRead() {
Log.w(TAG, "callDevice onRead");
try{Thread.sleep(1000);}catch(InterruptedException ex){}
WriteCharacteristic wc = new WriteCharacteristic(activity, context, getMacAddress(), serviceUUID, UUID.fromString(myUUID), ""){
#Override
public void onWrite(){
Log.w(TAG, "callDevice onWrite");
}
#Override
public void onError(){
Log.w(TAG, "callDevice onWrite-onError");
}
};
// Store data in writeBuffer
wc.writeCharacteristic(writeBuffer);
}
#Override
public void onError(){
Log.w(TAG, "callDevice onRead-onError");
}
};
rc.readCharacteristic();
My ReadCharacteristic implementation is as follows :
public class ReadCharacteristic extends BluetoothGattCallback {
public ReadCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag) {
mMacAddress = macAddress;
mService = service;
mCharacteristic = characteristic;
mTag = tag;
mContext = context;
this.activity =activity;
final BluetoothManager bluetoothManager =
(BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
final private static String TAG = "ReadCharacteristic";
private Object mTag;
private String mMacAddress;
private UUID mService;
private UUID mCharacteristic;
private byte[] mValue;
private Activity activity;
private BluetoothAdapter mBluetoothAdapter;
private Context mContext;
private int retry = 5;
public String getMacAddress() {
return mMacAddress;
}
public UUID getService() {
return mService;
}
public UUID getCharacteristic() {
return mCharacteristic;
}
public byte[] getValue() { return mValue; }
public void onRead() {
Log.w(TAG, "onRead: " + getDataHex(getValue()));
}
public void onError() {
Log.w(TAG, "onError");
}
public void readCharacteristic(){
if (retry == 0)
{
onError();
return;
}
retry--;
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(getMacAddress());
if (device != null) {
Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]");
final ReadCharacteristic rc = ReadCharacteristic.this;
device.connectGatt(mContext, false, rc);
}
}
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]");
if ((newState == 2)&&(status ==0)) {
gatt.discoverServices();
}
else{
Log.w(TAG, "[" + status + "]");
// gatt.disconnect();
gatt.close();
try
{
Thread.sleep(2000);
}
catch(Exception e)
{
}
readCharacteristic();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.w(TAG,"onServicesDiscovered [" + status + "]");
BluetoothGattService bgs = gatt.getService(getService());
if (bgs != null) {
BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic());
gatt.readCharacteristic(bgc);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.w(TAG,"onCharacteristicRead [" + status + "]");
if (status == BluetoothGatt.GATT_SUCCESS) {
mValue = characteristic.getValue();
Log.w(TAG,"onCharacteristicRead [" + mValue + "]");
gatt.disconnect();
gatt.close();
onRead();
}
else {
gatt.disconnect();
gatt.close();
}
}
}
This current method works perfectly fine for devices running KitKat and below. But when I run the same function on Lollipop, it beeps the device a couple of times and then stops working. From then on wards, whenever I try to connect, it says the device is disconnected and gives me an error code of 257 in OnConnectionStateChanged method.
I also get this error whenever I call this method -
04-20 14:14:23.503 12329-12384/com.webble.xy W/BluetoothGatt﹕ Unhandled exception in callback
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:446)
IS there anyone who has faced the same problem? I never encountered the object to be null when ever I tried debugging.
The problem has been reported to Google as Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing.
A workaround is to call disconnect() when you want to close BLE connection - and then only call close() in the onConnectionStateChange callback:
public void shutdown() {
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
}
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt,
int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
Here is my full source code (a class doing normal scan, directed scan - and discovering services):
public class BleObject {
public static final String ACTION_BLUETOOTH_ENABLED = "action.bluetooth.enabled";
public static final String ACTION_BLUETOOTH_DISABLED = "action.bluetooth.disabled";
public static final String ACTION_DEVICE_FOUND = "action.device.found";
public static final String ACTION_DEVICE_BONDED = "action.device.bonded";
public static final String ACTION_DEVICE_CONNECTED = "action.device.connected";
public static final String ACTION_DEVICE_DISCONNECTED = "action.device.disconnected";
public static final String ACTION_POSITION_READ = "action.position.read";
public static final String EXTRA_BLUETOOTH_DEVICE = "extra.bluetooth.device";
public static final String EXTRA_BLUETOOTH_RSSI = "extra.bluetooth.rssi";
private Context mContext;
private IntentFilter mIntentFilter;
private LocalBroadcastManager mBroadcastManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private BluetoothLeScanner mScanner;
private ScanSettings mSettings;
private List<ScanFilter> mScanFilters;
private Handler mConnectHandler;
public BleObject(Context context) {
mContext = context;
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Log.d(TAG, "BLE not supported");
return;
}
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.d(TAG, "BLE not accessible");
return;
}
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
mScanFilters = new ArrayList<ScanFilter>();
mConnectHandler = new Handler();
mBroadcastManager = LocalBroadcastManager.getInstance(context);
}
public boolean isEnabled() {
return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
processResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result: results) {
processResult(result);
}
}
private void processResult(ScanResult result) {
if (result == null)
return;
BluetoothDevice device = result.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_FOUND);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
i.putExtra(Utils.EXTRA_BLUETOOTH_RSSI, result.getRssi());
mBroadcastManager.sendBroadcast(i);
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
if (gatt == null)
return;
BluetoothDevice device = gatt.getDevice();
if (device == null)
return;
Log.d(TAG, "BluetoothProfile.STATE_CONNECTED: " + device);
Intent i = new Intent(Utils.ACTION_DEVICE_CONNECTED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "BluetoothProfile.STATE_DISCONNECTED");
Intent i = new Intent(Utils.ACTION_DEVICE_DISCONNECTED);
mBroadcastManager.sendBroadcast(i);
// Issue 183108: https://code.google.com/p/android/issues/detail?id=183108
try {
gatt.close();
} catch (Exception e) {
Log.d(TAG, "close ignoring: " + e);
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (gatt == null)
return;
for (BluetoothGattService service: gatt.getServices()) {
Log.d(TAG, "service: " + service.getUuid());
for (BluetoothGattCharacteristic chr: service.getCharacteristics()) {
Log.d(TAG, "char: " + chr.getUuid());
}
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_TURNING_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF");
break;
}
case BluetoothAdapter.STATE_OFF: {
Log.d(TAG, "BluetoothAdapter.STATE_OFF");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_DISABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
case BluetoothAdapter.STATE_TURNING_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON");
break;
}
case BluetoothAdapter.STATE_ON: {
Log.d(TAG, "BluetoothAdapter.STATE_ON");
Intent i = new Intent(Utils.ACTION_BLUETOOTH_ENABLED);
mBroadcastManager.sendBroadcast(i);
break;
}
}
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
if (state == BluetoothDevice.BOND_BONDED &&
prevState == BluetoothDevice.BOND_BONDING) {
if (mBluetoothGatt != null) {
BluetoothDevice device = mBluetoothGatt.getDevice();
if (device == null)
return;
Intent i = new Intent(Utils.ACTION_DEVICE_BONDED);
i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
mBroadcastManager.sendBroadcast(i);
}
}
}
}
};
// scan for all BLE devices nearby
public void startScanning() {
Log.d(TAG, "startScanning");
mScanFilters.clear();
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
// scan for a certain BLE device and after delay
public void startScanning(final String address) {
Log.d(TAG, "startScanning for " + address);
mScanFilters.clear();
mScanFilters.add(new ScanFilter.Builder().setDeviceAddress(address).build());
// create the scanner here, rather than in init() -
// because otherwise app crashes when Bluetooth is switched on
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mScanner.startScan(mScanFilters, mSettings, mScanCallback);
}
public void stopScanning() {
Log.d(TAG, "stopScanning");
if (mScanner != null) {
mScanner.stopScan(mScanCallback);
mScanner = null;
}
mScanFilters.clear();
}
public void connect(final BluetoothDevice device) {
Log.d(TAG, "connect: " + device.getAddress() + ", mBluetoothGatt: " + mBluetoothGatt);
mConnectHandler.post(new Runnable() {
#Override
public void run() {
setPin(device, Utils.PIN);
mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
}
});
}
private void setPin(BluetoothDevice device, String pin) {
if (device == null || pin == null || pin.length() < 4)
return;
try {
device.setPin(pin.getBytes("UTF8"));
} catch (Exception e) {
Utils.logw("setPin ignoring: " + e);
}
}
// called on successful device connection and will toggle reading coordinates
public void discoverServices() {
if (mBluetoothGatt != null)
mBluetoothGatt.discoverServices();
}
public boolean isBonded(BluetoothDevice device) {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
if (bondedDevices == null || bondedDevices.size() == 0)
return false;
for (BluetoothDevice bondedDevice: bondedDevices) {
Log.d(TAG, "isBonded bondedDevice: " + bondedDevice);
if (bondedDevice.equals(device)) {
Log.d(TAG, "Found bonded device: " + device);
return true;
}
}
return false;
}
public void startup() {
try {
mContext.registerReceiver(mReceiver, mIntentFilter);
} catch (Exception e) {
Log.d(TAG, "registerReceiver ignoring: " + e);
}
}
public void shutdown() {
Log.d(TAG, "BleObject shutdown");
try {
mContext.unregisterReceiver(mReceiver);
} catch (Exception e) {
Log.d(TAG, "unregisterReceiver ignoring: " + e);
}
try {
stopScanning();
} catch (Exception e) {
Log.d(TAG, "stopScanning ignoring: " + e);
}
try {
mBluetoothGatt.disconnect();
} catch (Exception e) {
Log.d(TAG, "disconnect ignoring: " + e);
}
mConnectHandler.removeCallbacksAndMessages(null);
}
}
On each BLE event it broadcasts intents via LocalBroadcastManager.
Thanks for pointing the problem Shashank.
I have looked at the Google example and followed what they recommend, it works like a charm with my device.
You should call the close() function in your onUnbind and the disconnect() when you need to, for example when you quit your application.
My Samsung device running KitKat(19) has a "Screen Mirroring" option which finds and syncs with nearby compatible displays. I would like to implement this feature programatically.
I have experimented with both the MediaRoute and DisplayManager classes. Both succeed in detecting nearby displays, but only AFTER I manually turn on screen mirroring. I would like them to detect displays automatically. Is there something I'm missing to initiate a scan or to kick WifiDisplayController into gear?
Here is my implementation of MediaRouter:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
output = (TextView) findViewById(R.id.output);
mMediaRouter = MediaRouter.getInstance(this);
mSelector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build();
}
#Override
public void onResume() {
super.onResume();
mMediaRouter.addCallback(mSelector, mMRCallback,
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
}
#Override
public void onPause() {
super.onPause();
mMediaRouter.removeCallback(mMRCallback);
}
protected void onDestroy() {
super.onDestroy();
}
private MediaRouter.Callback mMRCallback = new MediaRouter.Callback() {
#Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
super.onRouteAdded(router, route);
String outputText = router.getRoutes().size() + " Routes Detected\n";
List routes = router.getRoutes();
for (int i=0;i<routes.size(); i++) {
outputText = outputText + routes.get(i) + "\n";
}
output.setText(outputText);
}
#Override
public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
super.onRouteRemoved(router, route);
output.setText("Route Removed: " + route + "\n"
+ router.getRoutes().size() + " routes detected.");
}
#Override
public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) {
super.onProviderAdded(router, provider);
String outputText = router.getProviders().size() + " Providers Detected\n";
List providers = router.getProviders();
for (int i=0;i<providers.size(); i++) {
outputText = outputText + providers.get(i) + "\n";
}
output.setText(outputText);
}
#Override
public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) {
super.onProviderRemoved(router, provider);
output.setText("Provider removed: " + provider + "\n"
+ router.getProviders().size() + " providers detected.");
}
#Override
public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) {
super.onProviderChanged(router, provider);
}
};
And here is my implementation of DisplayManager:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
displayManager = (DisplayManager)
getSystemService(DISPLAY_SERVICE);
}
#Override
public void onResume() {
super.onResume();
displayManager.registerDisplayListener(displayListener,handler);
displayManager.getDisplays();
}
#Override
public void onPause() {
super.onPause();
displayManager.unregisterDisplayListener(displayListener);
}
private DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() {
#Override
public void onDisplayAdded(int displayId) {
output.setText("Display added: " + displayId);
}
#Override
public void onDisplayRemoved(int displayId) {
output.setText("Display removed: " + displayId);
}
#Override
public void onDisplayChanged(int displayId) {
}
};
I hope this will help you, done screen mirroring with the cast settings, it uses your device's cast service.
public void enablingWiFiDisplay() {
try {
startActivity(new Intent("android.settings.WIFI_DISPLAY_SETTINGS"));
return;
} catch (ActivityNotFoundException activitynotfoundexception) {
activitynotfoundexception.printStackTrace();
}
try {
startActivity(new Intent("android.settings.CAST_SETTINGS"));
return;
} catch (Exception exception1) {
Toast.makeText(getApplicationContext(), "Device not supported", Toast.LENGTH_LONG).show();
}
}