How to use 3G Connection in Android Application instead of Wi-fi?
I want to connect a 3G connection, is there any sample code to connect to 3G instead of Wi-fi?
/**
* Enable mobile connection for a specific address
* #param context a Context (application or activity)
* #param address the address to enable
* #return true for success, else false
*/
private boolean forceMobileConnectionForAddress(Context context, String address) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == connectivityManager) {
Log.debug(TAG_LOG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
//check if mobile connection is available and connected
State state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.debug(TAG_LOG, "TYPE_MOBILE_HIPRI network state: " + state);
if (0 == state.compareTo(State.CONNECTED) || 0 == state.compareTo(State.CONNECTING)) {
return true;
}
//activate mobile connection in addition to other connection already activated
int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
Log.debug(TAG_LOG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);
//-1 means errors
// 0 means already enabled
// 1 means enabled
// other values can be returned, because this method is vendor specific
if (-1 == resultInt) {
Log.error(TAG_LOG, "Wrong result of startUsingNetworkFeature, maybe problems");
return false;
}
if (0 == resultInt) {
Log.debug(TAG_LOG, "No need to perform additional network settings");
return true;
}
//find the host name to route
String hostName = StringUtil.extractAddressFromUrl(address);
Log.debug(TAG_LOG, "Source address: " + address);
Log.debug(TAG_LOG, "Destination host address to route: " + hostName);
if (TextUtils.isEmpty(hostName)) hostName = address;
//create a route for the specified address
int hostAddress = lookupHost(hostName);
if (-1 == hostAddress) {
Log.error(TAG_LOG, "Wrong host address transformation, result was -1");
return false;
}
//wait some time needed to connection manager for waking up
try {
for (int counter=0; counter<30; counter++) {
State checkState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (0 == checkState.compareTo(State.CONNECTED))
break;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//nothing to do
}
boolean resultBool = connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
Log.debug(TAG_LOG, "requestRouteToHost result: " + resultBool);
if (!resultBool)
Log.error(TAG_LOG, "Wrong requestRouteToHost result: expected true, but was false");
return resultBool;
}
And this for calculate host address:
/**
* This method extracts from address the hostname
* #param url eg. http://some.where.com:8080/sync
* #return some.where.com
*/
public static String extractAddressFromUrl(String url) {
String urlToProcess = null;
//find protocol
int protocolEndIndex = url.indexOf("://");
if(protocolEndIndex>0) {
urlToProcess = url.substring(protocolEndIndex + 3);
} else {
urlToProcess = url;
}
// If we have port number in the address we strip everything
// after the port number
int pos = urlToProcess.indexOf(':');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have resource location in the address then we strip
// everything after the '/'
pos = urlToProcess.indexOf('/');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
// If we have ? in the address then we strip
// everything after the '?'
pos = urlToProcess.indexOf('?');
if (pos >= 0) {
urlToProcess = urlToProcess.substring(0, pos);
}
return urlToProcess;
}
/**
* Transform host name in int value used by {#link ConnectivityManager.requestRouteToHost}
* method
*
* #param hostname
* #return -1 if the host doesn't exists, elsewhere its translation
* to an integer
*/
private static int lookupHost(String hostname) {
InetAddress inetAddress;
try {
inetAddress = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return -1;
}
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24)
| ((addrBytes[2] & 0xff) << 16)
| ((addrBytes[1] & 0xff) << 8 )
| (addrBytes[0] & 0xff);
return addr;
}
And following permission must be added to AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
It works only with android 2.2 and above, tested on both Nexus One and on LG Optimus, other phones I don't know because some method of ConnectivityMananger are vendor-specific.
After 15-20 seconds of inactivity, mobile network is automatically disconnected.
The T-Mobile 'My Account" app does this, if you are connected to a WiFi connection it tells you that their program will not work over WiFi and then asks the user if they want to turn off the WiFi connection. If you choose "No" then the application exits, if you choose "Yes" then the app turns off your WiFi connection and then continues with starting up.
I think this is a good model to follow, it will ensure that your app is not being ran over WiFi and allows the user to decide if they want to turn off WiFi or not. An improvement on this model would be to turn wifi back on when the user navigates away from your app.
I haven't tested the following code, but it looks like it should work (modified from here)
use the following permissions in your manifest
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
and here is some actual code to turn wifi on/off
private WifiManager wifiManager;
#Override
public void onCreate(Bundle icicle)
{
....................
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(wifiManager.isWifiEnabled())
{
wifiManager.setWifiEnabled(false);
}
else
{
wifiManager.setWifiEnabled(true);
}
}
If you do not want to go down that route it looks like you might be able to tell the phone that you would prefer to use the mobile data network rather than the wifi network.
The Android ConnectivityManager offers a function setNetworkPreference. This function is not really documented as you can tell if you click the link. I would paly around with it though because the constants that are defined seem to hint that you can set this to either TYPE_MOBILE or TYPE_WIFI and there is a DEFAULT_NETWORK_PREFERENCE constant as well that is defined as being 0x00000001 which is the same as TYPE_WIFI. So try getting access to a ConnectivityManager by calling
Context.getSystemService(Context.CONNECTIVITY_SERVICE);
and then try using the setNetworkPreference() function.
It doesn't appear to require any permissions in the manifest but it might require the CHANGE_NETWORK_STATE permission or something along those lines.
If you do sue the setNetworkPreference function it would probably be best to also set the Network Preference back to its original values (received from getNetworkPreference)
I hope this helps.
I think that is not possible from Java. The system shuts down all mobile network based communication if connected to a wireless network. I think that you aren't allowed to start a 3G connection from you program.
Here is the code that works on API 21+ (Lollipop, Marshmallow..).
I prefer to use OkHttp with Network.getSocketFactory(), but Network.openURLConnection() also works fine.
private void doTest()
{
display("Requesting CELLULAR network connectivity...");
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback()
{
/**
* Called when the framework connects and has declared a new network ready for use.
* This callback may be called more than once if the {#link Network} that is
* satisfying the request changes.
*
* This method will be called on non-UI thread, so beware not to use any UI updates directly.
*
* #param network The {#link Network} of the satisfying network.
*/
#Override
public void onAvailable(final Network network)
{
display("Got available network: " + network.toString());
try
{
final InetAddress address = network.getByName("navalclash.com");
display("Resolved host2ip: " + address.getHostName() + " -> " + address.getHostAddress());
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
display("Do request test page from remote http server...");
if(okHttpClient == null)
{
okHttpClient = new OkHttpClient.Builder().socketFactory(network.getSocketFactory()).build();
}
Request request = new Request.Builder()
.url("http://navalclash.com")
.build();
try (Response response = okHttpClient.newCall(request).execute())
{
display("RESULT:\n" + response.body().string());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
});
}
Inspired by code in this ticket and using some parts of it, here is service that establishes hipri mobile and keeps it running.
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.State;
import android.os.IBinder;
import android.util.Log;
public class HipriService extends Service {
private AtomicBoolean enabledMobile = new AtomicBoolean(false);
public boolean enableMobileConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == cm) {
Log.d(TAG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
/*
* Don't do anything if we are connecting. On the other hands re-new
* connection if we are connected.
*/
State state = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.d(TAG, "TYPE_MOBILE_HIPRI network state: " + state);
if (0 == state.compareTo(State.CONNECTING))
return true;
/*
* Re-activate mobile connection in addition to other connection already
* activated
*/
int resultInt = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
//Log.d(TAG, "startUsingNetworkFeature for enableHIPRI result: " + resultInt);
//-1 means errors
// 0 means already enabled
// 1 means enabled
// other values can be returned, because this method is vendor specific
if (-1 == resultInt) {
Log.e(TAG, "Wrong result of startUsingNetworkFeature, maybe problems");
return false;
}
if (0 == resultInt) {
Log.d(TAG, "No need to perform additional network settings");
return true;
}
return requestRouteToHost(this, Uploader.ServerAddress);
}
private Thread pingerThread = null;
private void startMobileConnection() {
enabledMobile.set(true);
pingerThread = new Thread(new Runnable() {
#Override
public void run() {
while (enabledMobile.get()) {
/*
* Renew mobile connection. No routing setup is needed. This
* should be moved to 3g monitoring service one day.
*/
enableMobileConnection();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
}
}
});
pingerThread.start();
}
private void stopMobileConnection() {
enabledMobile.set(false);
disableMobileConnection();
pingerThread.interrupt();
pingerThread = null;
}
public void disableMobileConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
}
public final static int inetAddressToInt(InetAddress inetAddress) {
byte[] addrBytes;
int addr;
addrBytes = inetAddress.getAddress();
addr = ((addrBytes[3] & 0xff) << 24) | ((addrBytes[2] & 0xff) << 16) | ((addrBytes[1] & 0xff) << 8)
| (addrBytes[0] & 0xff);
return addr;
}
public final static InetAddress lookupHost(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
return null;
}
}
private boolean requestRouteToHost(Context context, String hostname) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null == cm) {
Log.d(TAG, "ConnectivityManager is null, cannot try to force a mobile connection");
return false;
}
/* Wait some time needed to connection manager for waking up */
try {
for (int counter = 0; enabledMobile.get() && counter < 30; counter++) {
State checkState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
Log.i(TAG, "Waiting for mobile data on. State " + checkState);
if (0 == checkState.compareTo(State.CONNECTED))
break;
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//nothing to do
}
if (!enabledMobile.get()) {
Log.d(TAG, "Mobile data is turned off while waiting for routing.");
return false;
}
State checkState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (0 != checkState.compareTo(State.CONNECTED)) {
Log.e(TAG, "Mobile data is still turned off after 30 sec of waiting.");
return false;
}
Log.i(TAG, "Adding routing for " + hostname);
InetAddress inetAddress = lookupHost(hostname);
if (inetAddress == null) {
Log.e(TAG, "Failed to resolve " + hostname);
return false;
}
int hostAddress = inetAddressToInt(inetAddress);
boolean resultBool = cm.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
Log.d(TAG, "requestRouteToHost result: " + resultBool);
if (!resultBool)
Log.e(TAG, "Wrong requestRouteToHost result: expected true, but was false");
return resultBool;
}
#Override
public void onCreate() {
super.onCreate();
startMobileConnection();
}
#Override
public void onDestroy() {
stopMobileConnection();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Here is how I start/stop it when needed. Note that it also gets locks on cpu and wifi so that it may run when the phone sleeps (screen only). Wifi is needed because my app is kind of bridge between wifi and mobile connections. You may not need it.
public void startMobileData() {
if (!enabledMobile.get()) {
enabledMobile.set(true);
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "Wifi Wakelock");
wifiLock.acquire();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "3G Wakelock");
partialLock.acquire();
startService(new Intent(this, HipriService.class));
}
}
public void stopMobileData() {
if (enabledMobile.get()) {
enabledMobile.set(false);
Log.i(TAG, "Disabled mobile data");
stopService(new Intent(this, HipriService.class));
if (partialLock != null) {
partialLock.release();
partialLock = null;
}
if (wifiLock != null) {
wifiLock.release();
wifiLock = null;
}
}
}
Don't forget to add service to you manifest file.
Use connection manager and set network preference as you want.
for example:
dataManager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
dataManager.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
This aplication activate 3G and Wifi connection, Giving preference to 3G!!
Very useful http://www.redrails.com.br/2012/02/wireless-analyzer-for-android/
#umka
i think, app can only reach to those hosts through HIPRI which it has requested, may be for other hosts(whose routes are not requested) is using the default network(MOBILE or WIFI).
On calling stopUsingNetworkFeature(), It will check -- is there any other app is using this network, if yes, then it will ignore your request to down this network feature.
one of the main purpose of HIPRI network is that - if wifi is on and an app wants to use mobile netwrok(3G)to reach the particular host, it can reach through HIPRI network.
There is a missing piece of code on #Northern Captain answer, the DNS lookup code.
Here is a working code:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d("NetworkDns", "Requesting CELLULAR network connectivity...");
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback()
{
#Override
public void onAvailable(final Network network)
{
Log.d("NetworkDns", "Got available network: " + network.toString());
try
{
final InetAddress address = network.getByName("www.website.com");
Log.d("NetworkDns", "Resolved host2ip: " + address.getHostName() + " -> " + address.getHostAddress());
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
Log.d("NetworkDns", "Do request test page from remote http server...");
OkHttpClient okHttpClient = null;
if(okHttpClient == null)
{
okHttpClient = new OkHttpClient.Builder()
.socketFactory(network.getSocketFactory())
.dns(new Dns() {
#Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
if (network != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
List<InetAddress> addresses = Arrays.asList(network.getAllByName(hostname));
Log.d("NetworkDns", "List : " + addresses);
return addresses;
}
return SYSTEM.lookup(hostname);
}
}).build();
}
Request request = new Request.Builder()
.url("http://www.website.com")
.build();
try (Response response = okHttpClient.newCall(request).execute())
{
Log.d("NetworkDns", "RESULT:\n" + response.body().string());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
});
}
Related
I have an android app to connect to a BLE device and write to it. I can successfully connect, read and write to it. As a part of testing, we are trying different disconnection scenarios.
Sometimes, if BLE device disconnects the connection, I get the connection change as disconnect with status value as 19. Also if there is any bond error, status equals 22. If I programmatically disconnect the connection, this status gives me 0. But none of these states except 0 are specified in android documentation.
Posting a sample BluetoothGattCallback
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(TAG, "onConnectionStateChange status: "+status+", newState: "+newState);
/*i need to know the possible values for this status variable*/
if(newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else {
gatt.close();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i(TAG, "onServicesDiscovered service discovered");
}
};
Does anyone face this same problem and sorted out the list of statuses. I need to know the possible values for status variable in onConnectionStateChange method
Here is the list of codes i have
Programmatically disconnected - 0
Device went out of range - 8
Disconnected by device - 19
Issue with bond - 22
Device not found - 133(some phone it gives 62)
I have tested disconnect scenario's in 5.0.2, 5.1, 6.0 and 6.0.1. But only found this bond issue code in 6.0.1 android version.
Sorry to bring up an old question, but here is the solution for many of the problems i've had with Bluetooth (BLE) 4.0. Sorry again for the big classes below but be sure they are needed and no method there is irrelevant or unused.
public abstract class AbstractBluetoothBroadcaster extends BroadcastReceiver {
protected static final String LOG_TAG = BluetoothLowEnergy.LOG_TAG;
protected BluetoothLowEnergy bluetoothLowEnergy;
public AbstractBluetoothBroadcaster(BluetoothLowEnergy bluetoothLowEnergy, String action){
super();
this.bluetoothLowEnergy = bluetoothLowEnergy;
IntentFilter intentFilterStateChange = new IntentFilter(action);
intentFilterStateChange.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
this.bluetoothLowEnergy.getActivity().registerReceiver(this, intentFilterStateChange);
}
public void onDestroy(){
this.bluetoothLowEnergy.getActivity().unregisterReceiver(this);
}
}
public class BluetoothBondStateBroadcaster extends AbstractBluetoothBroadcaster {
private BluetoothLowEnergy bluetoothLowEnergy;
private boolean deviceBonded;
public BluetoothBondStateBroadcaster(BluetoothLowEnergy bluetoothLowEnergy) {
super(bluetoothLowEnergy, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
this.bluetoothLowEnergy = bluetoothLowEnergy;
this.deviceBonded = false;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null){
return;
}
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED) &&
bluetoothDevice != null &&
bluetoothDevice.getAddress().equals(bluetoothLowEnergy.getDeviceUUID())) {
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
switch (state) {
case BluetoothDevice.BOND_NONE:
Log.d(LOG_TAG, " NOT BONDED - dev " + bluetoothDevice.getAddress());
this.deviceBonded = false;
break;
case BluetoothDevice.BOND_BONDING:
Log.d(LOG_TAG, " BONDING ... - dev " + bluetoothDevice.getAddress());
break;
case BluetoothDevice.BOND_BONDED:
Log.d(LOG_TAG, " BONDED - dev " + bluetoothDevice.getAddress());
deviceBonded = true;
bluetoothLowEnergy.onBluetoothBonded();
break;
default:
break;
}
}
}
public void resetDeviceBonded(){
this.deviceBonded = false;
}
public boolean isDeviceBonded() {
return deviceBonded;
}
}
public class BluetoothPairingBroadcaster extends AbstractBluetoothBroadcaster {
private String devicePIN;
public BluetoothPairingBroadcaster(BluetoothLowEnergy bluetoothLowEnergy){
super(bluetoothLowEnergy, BluetoothDevice.ACTION_PAIRING_REQUEST);
this.devicePIN = "";
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null){
return;
}
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int pairingType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST) &&
bluetoothDevice != null &&
bluetoothDevice.getAddress().equals(bluetoothLowEnergy.getDeviceUUID()) &&
!getDevicePIN().isEmpty()) {
if (pairingType == BluetoothDevice.PAIRING_VARIANT_PIN){
bluetoothDevice.setPin(getDevicePIN().getBytes());
Log.d(LOG_TAG," Auto-entering pin - " + getDevicePIN());
bluetoothDevice.createBond();
Log.d(LOG_TAG," pin entered and request sent...");
abortBroadcast();
}
}
}
public void setDevicePIN(String pin){
this.devicePIN = pin;
}
public String getDevicePIN(){
return this.devicePIN ;
}
}
public class BluetoothLowEnergy extends BluetoothGattCallback {
// listener that has the methods that the application (activity)
// will use to send / receive data, or to reflect the system state
// in the UI
public interface BluetoothListener {
/**
* Triggered when the scanning has started successfully
*/
void onBluetoothStartScan();
/**
* Triggered when the scanning stops
* #param scanResults results of the scanning
*/
void onBluetoothStopScan(Collection<BluetoothDevice> scanResults);
/**
* Triggered when the device is ready to send/receive data
*/
void onBluetoothConnectionReady();
/**
* Triggered when a bluetooth msg is received
* #param msg message received
*/
void onBluetoothReceiveMsg(String msg);
/**
* Triggered whenever data is send
* #param success true means data was sent fine to the remote device, false otherwise
*/
void onBluetoothSend(String data, boolean success);
/**
* Triggered if no bluetooth is connected, and we need a connection
* to send / receive / discover services
*/
void onBluetoothNotConnected();
}
// custom exceptions
public class BluetoothNotEnabledException extends Exception { }
public class BluetoothLowEnergyNotSupported extends Exception { }
public class BluetoothDeviceNotFound extends Exception { }
// service and characteristic uuids that are going to be used to
// send / receive data between central and peripheral GATTs
private static final String SERVICE_UUID = "FFE0-";
private static final String CHARACTERISTIC_UUID = "FFE1-";
// timeout for bluetooth scan (in ms)
public static final int SCAN_TIMEOUT = 5000;
// BLE LOG TAG
public static final String LOG_TAG = "BLUETOOTH_BLE";
// model
private boolean bluetoothScanning;
private boolean bluetoothConnected;
private Map<String, BluetoothDevice> bluetoothScanResults;
// gui
private Activity activity;
// bluetooth
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;
private ScanCallback bluetoothScanCallback;
private BluetoothGatt bluetoothGatt;
private BluetoothGattCharacteristic characteristic;
public BluetoothLowEnergy(Activity activity, BluetoothListener bluetoothListener){
this.activity = activity;
this.bluetoothListener = bluetoothListener;
// this keeps track of the scanning and connection states
this.bluetoothScanning = this.bluetoothConnected = false;
// keeps track of the scanning results
this.bluetoothScanResults = new HashMap<>();
// set bluetooth pairing request and bonded callback
// these broadcasters will be responsible to detect and validate
// the bonded state of your device
this.pairingRequestBroadcaster = new BluetoothPairingBroadcaster(this);
this.bondedBroadcaster = new BluetoothBondStateBroadcaster(this);
// set the scan callback methods that will add results to
// this.bluetoothScanResults map
this.bluetoothScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
addScanResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
for (ScanResult result: results) {
addScanResult(result);
}
}
#Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e(LOG_TAG, "Scan Failed with code " + errorCode);
}
private void addScanResult(ScanResult result) {
BluetoothDevice device = result.getDevice();
String deviceAddress = device.getAddress();
bluetoothScanResults.put(deviceAddress, device);
Log.d(LOG_TAG, "Found device " + deviceAddress);
}
};
// Use this to determine whether BLE is supported on the device.
if (!this.activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
throw new BluetoothLowEnergyNotSupported();
}
}
/**
* This method should be called when the activity is destroyed
*/
public void onDestroy(){
this.bondedBroadcaster.onDestroy();
this.pairingRequestBroadcaster.onDestroy();
this.disconnect();
}
/**
* This method is called when we finish pairing/bonding to the device
*/
public void onBluetoothBonded(){
// if we have the services already discovered, then we can
// send/receive data, to do so we call the bluetooth listener below
if (servicesDiscovered){
this.bluetoothListener.onBluetoothConnectionReady();
// if we know we have a connection established, then we can
// discover services
} else if (bluetoothConnected){
bluetoothGatt.discoverServices();
}
}
/**
* This method is called whenever a connection is established or a disconnection happens
*/
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
BluetoothDevice bluetoothDevice = gatt.getDevice();
// if these conditions == true, then we have a disconnect
if ( status == BluetoothGatt.GATT_FAILURE ||
status != BluetoothGatt.GATT_SUCCESS ||
newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(LOG_TAG, String.format(Locale.getDefault(),
"Disconnected from %s (%s) - status %d - state %d",
bluetoothDevice.getName(),
bluetoothDevice.getAddress(),
status,
newState
));
this.disconnect();
// if these conditions == true, then we have a successful connection
} else if (newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothConnected = true;
Log.d(LOG_TAG, String.format(Locale.getDefault(),
"Connected to %s (%s) - status %d - state %d",
bluetoothDevice.getName(),
bluetoothDevice.getAddress(),
status,
newState
));
// this sleep is here to avoid TONS of problems in BLE, that occur whenever we start
// service discovery immediately after the connection is established
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
gatt.discoverServices();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status != BluetoothGatt.GATT_SUCCESS) {
return;
}
// BEGIN - find the service and characteristic that we want (defined as a static attribute
// of the BluetoothLowEnergy class)
Log.d(LOG_TAG, "Discovering services ...");
BluetoothGattService service = null;
for (BluetoothGattService serv: gatt.getServices()){
Log.d(LOG_TAG, "Found service " + serv.getUuid().toString());
if (serv.getUuid().toString().toUpperCase().contains(SERVICE_UUID)){
service = serv;
Log.d(LOG_TAG, "---> Selected service " + serv.getUuid().toString());
break;
}
}
if (service == null){
return;
}
for (BluetoothGattCharacteristic charac: service.getCharacteristics()){
Log.d(LOG_TAG, "Found characteristic " + charac.getUuid().toString());
if (charac.getUuid().toString().toUpperCase().contains(CHARACTERISTIC_UUID)){
this.characteristic = charac;
Log.d(LOG_TAG, "---> Selected characteristic " + charac.getUuid().toString());
break;
}
}
if (this.characteristic == null){
return;
}
Log.d(LOG_TAG, "Setting write and notification to the characteristic ...");
bluetoothAdapter.cancelDiscovery();
// END - find the service and characteristic
// set that we want to write to the selected characteristic and be notified if
// it changes (the remote GATT peripheral sends data to the Android's GATT Center)
this.characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
gatt.setCharacteristicNotification(this.characteristic, true);
// we finished service discovery
this.servicesDiscovered = true;
// if we have paired/bonded then we are ready to send/receive data
if (pairingRequestBroadcaster.getDevicePIN().isEmpty() || bondedBroadcaster.isDeviceBonded()) {
this.bluetoothListener.onBluetoothConnectionReady();
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int status) {
super.onCharacteristicRead(gatt, charac, status);
restartDisconnectTimeout();
if (status != BluetoothGatt.GATT_SUCCESS) {
return;
}
try {
String characValue = new String(charac.getValue(), CHARSET)
.replaceAll(DATA_FILTER_REGEX,"");
Log.i(LOG_TAG, String.format(Locale.getDefault(),
"Characteristic Read - %s",
characValue
));
if (charac.getUuid().equals(this.characteristic.getUuid())) {
this.bluetoothListener.onBluetoothReceiveMsg(characValue);
}
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Characteristic Read - Failed to convert message string to byte array");
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic charac, int status) {
super.onCharacteristicWrite(gatt, charac, status);
restartDisconnectTimeout();
try {
String characValue = new String(charac.getValue(), CHARSET);
Log.i(LOG_TAG, String.format(Locale.getDefault(),
"Characteristic Write - SUCCESS - %s",
characValue
));
bluetoothListener.onBluetoothSend( characValue, (status == BluetoothGatt.GATT_SUCCESS) );
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG, "Characteristic Write - Failed to convert message string to byte array");
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic charac) {
super.onCharacteristicChanged(gatt, charac);
Log.d(LOG_TAG,"Characteristic Changed");
onCharacteristicRead(gatt, charac, BluetoothGatt.GATT_SUCCESS);
}
/**
* Remove pairing/bonding of the device
* #param device Device to remove bonding
*/
public static void removeBond(BluetoothDevice device){
try {
if (device == null){
throw new Exception();
}
Method method = device.getClass().getMethod("removeBond", (Class[]) null);
method.invoke(device, (Object[]) null);
Log.d(LOG_TAG, "removeBond() called");
Thread.sleep(600);
Log.d(LOG_TAG, "removeBond() - finished method");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Clears the GATT services cache, so that new services can be discovered
* #param bluetoothGatt GATT Client to clear service's discovery cache
*/
public static void refresh(BluetoothGatt bluetoothGatt){
try {
Method method = bluetoothGatt.getClass().getMethod("refresh", (Class[]) null);
method.invoke(bluetoothGatt, (Object[]) null);
} catch (Exception e){
e.printStackTrace();
}
}
/**
* Connect to the GATT Peripheral device
* #param uuid GATT Peripheral address / mac / uuid to connect to
* #param pin PIN to authenticate and pair to the device
*/
public void connect(String uuid, String pin) throws BluetoothNotEnabledException, BluetoothDeviceNotFound {
checkBluetooth();
// do not connect twice
if (this.isConnected()){
return;
}
// get device
BluetoothDevice device = this.bluetoothScanResults.get(uuid);
if (device == null){
throw new BluetoothDeviceNotFound();
}
this.deviceUUID = uuid;
pairingRequestBroadcaster.setDevicePIN(pin);
removeBond(device);
// create connection to the bluetooth device
bluetoothGatt = device.connectGatt(activity, false, this);
refresh(bluetoothGatt);
}
/**
* Disconnect from BLE device. This method should be called whenever we want to
* close the APP, or the BLE connection.
*/
public void disconnect() {
Log.d(LOG_TAG, "disconnect() - executed");
if (bluetoothGatt != null) {
if (characteristic != null) {
bluetoothGatt.setCharacteristicNotification(characteristic, false);
}
//remove device authorization/ bond/ pairing
removeBond(bluetoothGatt);
// disconnect now
bluetoothGatt.disconnect();
bluetoothGatt.close();
Log.d(LOG_TAG, "disconnect() - bluetoothGatt disconnect happened");
}
bluetoothGatt = null;
characteristic = null;
bluetoothConnected = false;
servicesDiscovered = false;
// set device as not bonded anymore
bondedBroadcaster.resetDeviceBonded();
}
/**
* bluetooth nearby devices scan is on
* #return true if scanning is on, false otherwise
*/
public boolean isScanning(){
return (this.bluetoothScanning);
}
/**
* Check bluetooth system state (on or off)
* #return true if system is on, false otherwise
*/
public boolean isEnabled(){
try {
checkBluetooth();
return bluetoothAdapter.isEnabled();
} catch (BluetoothNotEnabledException e) {
return false;
}
}
/**
* Check bluetooth connection
* #return true if connected, false otherwise
*/
public boolean isConnected(){
return (this.bluetoothConnected);
}
/**
* Start bluetooth scan for nearby devices
* #param filters Scan filters that define what devices to scan for
*/
public void startScan(List<ScanFilter> filters)
throws BluetoothNotEnabledException{
checkBluetooth();
// dont run two scans simultaneously
if (isScanning()) {
return;
}
// disconnect previously connected devices
if (isConnected()) {
this.disconnect();
return;
}
// setup bluetooth scanning settings
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
// start scanning
this.bluetoothScanning = true;
this.bluetoothScanResults.clear();
this.bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
// Stops scanning after a pre-defined scan period.
Handler bluetoothHandler = new Handler();
bluetoothHandler.postDelayed(new Runnable() {
#Override
public void run() {
stopScan();
}
}, SCAN_TIMEOUT);
// start scan with default scan callback
this.bluetoothLeScanner.startScan(filters, settings, bluetoothScanCallback);
// we have started successfully the BLE scanning
bluetoothListener.onBluetoothStartScan();
}
/**
* Stop bluetooth scan for nearby devices
*/
public void stopScan(){
if (!bluetoothScanning) {
return;
}
// set app scan state to false
bluetoothScanning = false;
if (bluetoothLeScanner != null) {
bluetoothLeScanner.stopScan(bluetoothScanCallback);
bluetoothLeScanner = null;
}
// we have stopped BLE scanning, call the user's callback
bluetoothListener.onBluetoothStopScan(bluetoothScanResults.values());
}
/**
* Send a message via bluetooth
* #param msg message to send
*/
public void send(String msg) {
if (!bluetoothConnected || characteristic == null){
bluetoothListener.onBluetoothNotConnected();
return;
}
try {
msg = msg.replaceAll(DATA_FILTER_REGEX, "") + TERMINATION_CHAR;
Log.d(LOG_TAG, String.format(Locale.getDefault(),
"Sending message: %s",
msg));
characteristic.setValue(msg.getBytes(CHARSET));
bluetoothGatt.writeCharacteristic(characteristic);
} catch (UnsupportedEncodingException e) {
Log.e(LOG_TAG,
"BluetoothLowEnergy.send: Failed to convert message string to byte array");
}
}
public String getDeviceUUID(){
return deviceUUID;
}
public Activity getActivity(){
return activity;
}
/**
* Check if bluetooth is enabled and working properly
*/
private void checkBluetooth() throws BluetoothNotEnabledException{
if (bluetoothAdapter == null) {
final BluetoothManager bluetoothManager =
(BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager == null){
throw new BluetoothNotEnabledException();
}
bluetoothAdapter = bluetoothManager.getAdapter();
}
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
throw new BluetoothNotEnabledException();
}
}
}
The key methods and functions to avoid problems used above are:
Thread.sleep(600)
removeBond(device)
refresh(gatt)
gatt.disconnect()
gatt.close()
In my case I got this response from bluetooth stack because the device was already bonded with my phone. I removed it from my settings and the error 22 vanished.
in aosp (android source code). you can find any error in bluetooth source code, and know the meaning of status code.
the file path is system/bt/stack/include/gatt_api.h
Here's the link: https://android.googlesource.com/platform/system/bt/+/ea7ab70a711e642653dd5922b83aa04a53af9e0e/stack/include/gatt_api.h but it all display by hex.
for example:
hex
Decimal
reason
0x08
8
connection timeout
0x13
19
connection terminate by peer user
0x16
22
connectionterminated by local host
0x22
34
connection fail for LMP response tout
0x85
133
gatt_error
I am trying to write and android application that scans for BLE devices and when it finds the certain devices with a naming scheme it connects to it and reads a characteristic (A user defined name for the device) then disconnects right after. It would then display the device in a list with any other devices found and read the user defined name. The user can then chose a device to connect to (Or multiple devices) and connect to it and stream data from it.
The problem that keeps happening is after it gets the user defined name and is disconnected the BLE devices stop broadcasting and I can no longer find it when I scan or if I try to connect to it after I read the user defined name and disconnected to it.
Is this an issue with the Android BLE stack or do I need to add more delays (I have 100 millisecond delays throughout the bluetoothservice I use)
Here is part of the code I use in my service
public boolean initialize() {
Log.i(TAG, "Initializing");
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
mReadyToWrite = true;
mReadyToRead = true;
mReady = true;
mCharacteristicWriteQueue = new ArrayDeque<BluetoothGattCharacteristic>();
mCharacteristicReadQueue = new ArrayDeque<BluetoothGattCharacteristic>();
mDescriptorWriteQueue = new ArrayDeque<BluetoothGattDescriptor>();
mDescriptorReadQueue = new ArrayDeque<BluetoothGattDescriptor>();
//mBluetoothGattMap = new HashMap<String, BluetoothGatt>();
return true;
}
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* #param address The device address of the device.
*
* #return Return true if the connection is initiated successfully. The connection result
* is reported asynchronously through the
* {#code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
if(mBluetoothGattMap.containsKey(address)) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGattMap.get(address).connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
mBluetoothGattMap.put(address, device.connectGatt(this, false, mGattCallback));
Log.d(TAG, "Trying to create a new connection to address " + address);
//mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {#code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect(String address) {
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
Log.i(TAG, "Disconnecting from gatt");
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
mBluetoothGattMap.get(address).disconnect();
}
public void close(String address) {
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
mBluetoothGattMap.get(address).close();
mBluetoothGattMap.remove(address);
Log.w(TAG, "Succeeed removing it");
}
public int getConnectionState(String address) {
Log.i(TAG, "getting connection state for " + address);
BluetoothGatt gatt = mBluetoothGattMap.get(address);
return mBluetoothManager.getConnectionState(gatt.getDevice(), BluetoothProfile.GATT);
}
/**
* Request a read on a given {#code BluetoothGattCharacteristic}. The read result is reported
* asynchronously through the {#code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* #param characteristic The characteristic to read from.
*/
public void readCharacteristic(String address, BluetoothGattCharacteristic characteristic) {
Log.i(TAG, "reading characteristic");
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
if(mReadyToRead && mReady) {
boolean result = mBluetoothGattMap.get(address).readCharacteristic(characteristic);
mReadyToRead = false;
mReady = false;
if(!result) {
Log.i(TAG, "read failed");
}
}else {
mCharacteristicReadQueue.push(characteristic);
}
}
public void writeCharacteristic(String address, BluetoothGattCharacteristic characteristic) {
Log.i(TAG, "writeCharacteristic - readyToWrite = " + mReadyToWrite + " queue size = " + mCharacteristicWriteQueue.size());
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
if(mReadyToWrite && mReady) {
boolean result = mBluetoothGattMap.get(address).writeCharacteristic(characteristic);
mReadyToWrite = false;
mReady = false;
if(!result) {
Log.i(TAG, "characteristic write failed");
}
}else {
mCharacteristicWriteQueue.push(characteristic);
}
}
public void readDescriptor(String address, BluetoothGattDescriptor descriptor) {
Log.i(TAG, "reading descriptor");
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
if(mReadyToRead && mReady) {
boolean result = mBluetoothGattMap.get(address).readDescriptor(descriptor);
mReadyToRead = false;
mReady = false;
if(!result) {
Log.i(TAG, "descriptor read failed");
}
}else {
mDescriptorReadQueue.push(descriptor);
}
}
public void writeDescriptor(String address, BluetoothGattDescriptor descriptor) {
Log.i(TAG, "writing descriptor for characteristic " + descriptor.getCharacteristic().getUuid().toString());
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
if(mReadyToWrite && mReady) {
boolean result = mBluetoothGattMap.get(address).writeDescriptor(descriptor);
mReadyToWrite = false;
mReady = false;
if(!result) {
Log.i(TAG, "descriptor write failed");
}
}else {
mDescriptorWriteQueue.push(descriptor);
}
}
public BluetoothGattCharacteristic getCharacteristic(String address, UUID uuid) {
if(!mBluetoothGattMap.containsKey(address)) {
Log.i(TAG, "Device address " + address + " not found");
return null;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
for(BluetoothGattService service : mBluetoothGattMap.get(address).getServices()) {
Log.i(TAG, "Service: " + service.getUuid().toString());
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
Log.i(TAG, "Characteristic: " + characteristic.getUuid().toString());
if(characteristic.getUuid().equals(uuid)) {
return characteristic;
}
}
}
Log.i(TAG, "Characteristic not found");
return null;
}
public Set<String> getConnectedDevices(){
return this.mBluetoothGattMap.keySet();
}
/**
* Enables or disables notification on a give characteristic.
*
* #param characteristic Characteristic to act on.
* #param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(String address, BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || !mBluetoothGattMap.containsKey(address)) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}catch(InterruptedException e){
//ignore
}
mBluetoothGattMap.get(address).setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHAR_CONFIG));
if(descriptor != null) {
boolean status = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
Log.i(TAG, "descriptor " + descriptor.getUuid().toString() + " setValue() status: " + status);
Log.i(TAG, "descriptor value: " + descriptor.getValue());
writeDescriptor(address, descriptor);
}
}
public void setPhoneEvents(byte priorities) {
for(String address : mBluetoothGattMap.keySet()) {
BluetoothGattCharacteristic characteristic = getCharacteristic(address, UUID.fromString(GattAttributes.ALERT_ATTRIBUTE));
if (characteristic != null) {
byte prioritiesBuf[] = new byte[1];
prioritiesBuf[0] = priorities;
characteristic.setValue(prioritiesBuf);
writeCharacteristic(address, characteristic);
Log.i(TAG, String.format("Forwarded phone alert priorities: 0x%X", priorities));
} else {
Log.e(TAG, "Failed to get the Alert ID characteristic from Gatt Server for device address " + address);
}
}
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {#code BluetoothGatt#discoverServices()} completes successfully.
*
* #return A {#code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices(String address) {
if (mBluetoothGattMap.get(address) == null) return null;
return mBluetoothGattMap.get(address).getServices();
}
The problem that keeps happening is after it gets the user defined
name and is disconnected the BLE devices stop broadcasting and I can
no longer find it when I scan or if I try to connect to it after I
read the user defined name and disconnected to it.
Shouldn't be your BLE device need start advertising soon after it's connection dropped?
I would also suggest that you can let your BLE device advertise your customized service other than your app to connect then read the characteristic; you just use "ScanFilter" to filter out what your favorite devices.
You just let low level code do to this.
The observed behaviour is really a feature of the BLE Peripheral device and not Android, or more generally, the Central device program.
I've recently been working with a Laird BL600 - and it has an 'Over the Air', OTA, programming mode - and when that mode is enabled the module advertises the OTA service for up to 10 seconds after being powered on: and that's it. The Peripheral application in this case has been desinged not to drop back to advertising after any connection is broken. To re-enter OTA mode a power cycle of the device is intended.
As suggested by Guo, if there is control over the Peripheral program, an easier scheme would be for the BLE Peripheral to include a readable identifier (device name) and the GUID of its main service in its advertising packet - which would make filtering which devices to present to the user easier.
When the user wishes to connect to the Peripheral, then that will only succeed using device.connectGatt(this, false, mGattCallback)
if the device is still advertising and in range. I think the intention of the second parameter in this call - 'autoconnect' - is to mark the connection, within Android, as pending - and that the connection should happen automatically when the device is back in range and advertising - but I've not found this to be very reliable.
I'am able to connect to device and ask device specifications:
specs:
Model: /dev/bus/usb/001/002
ID: 1002
Class: 0
Protocol: 0
Vendor ID 1155
Product ID: 22352
Interface count: 1
---------------------------------------
***** *****
Interface index: 0
Interface ID: 0
Inteface class: 3 USB_CLASS_HID
Interface protocol: 0
Endpoint count: 2
++++ ++++ ++++
Endpoint index: 0
Attributes: 3
Direction: 128 (device to host)
Number: 1
Interval: 1
Packet size: 64
Type: 3 USB_ENDPOINT_XFER_INT (interrupt endpoint)
++++ ++++ ++++
Endpoint index: 1
Attributes: 3
Direction: 0 (host to device)
Number: 1
Interval: 1
Packet size: 64
Type: 3 USB_ENDPOINT_XFER_INT (interrupt endpoint)
No more devices connected.
I'am also able to send data from host to device, using this method:
int bufferDataLength = mEndpointOut.getMaxPacketSize();
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
UsbRequest request = new UsbRequest();
buffer.put(DataToSend);
request.initialize(mDeviceConnection, mEndpointOut);
request.queue(buffer, bufferDataLength);
try
{
if (request.equals(mDeviceConnection.requestWait()))
{
// Read an analyze the incoming data here
byte[] byteBuffer = new byte[buffer.remaining()];
buffer.get(byteBuffer, 0, buffer.remaining());
return true;
}
}
catch (Exception ex)
{
Log.e(TAG, "Error sending data: " + ex.toString());
}
return false;
But i'am not able to read from device, i've tested several methods without success..
for example this doesn't work:
int bufferDataLength = mEndpointIn.getMaxPacketSize();
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength + 1);
//Make a request
UsbRequest request = new UsbRequest();
request.initialize(mDeviceConnection, mEndpointIn);
ByteBuffer buffer = ByteBuffer.allocate(bufferDataLength);
//For IN endpoints, data is read into the buffer
request.queue(buffer, bufferDataLength);
//This blocks until the request is successful
//Make sure the request that finished is the one you need
if (mDeviceConnection.requestWait() == request) {
// Read an analyze the incoming data here
byte[] byteBuffer = new byte[buffer.remaining()];
buffer.get(byteBuffer, 0, buffer.remaining());
}
Suggestions?
I resolved my problems using this class, i'm answering to my my question hoping it can help someone else:
/**
* This class is used for talking to hid of the dongle, connecting, disconnencting and enumerating the devices.
* #author gai
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public class HidBridge {
private Context _context;
private int _productId;
private int _vendorId;
//private HidBridgeLogSupporter _logSupporter = new HidBridgeLogSupporter();
private static final String ACTION_USB_PERMISSION =
"com.example.company.app.testhid.USB_PERMISSION";
// Locker object that is responsible for locking read/write thread.
private final Object _locker = new Object();
private Thread _readingThread = null;
private boolean _runReadingThread = false;
private String _deviceName;
private UsbManager _usbManager;
private UsbDevice _usbDevice;
// The queue that contains the read data.
private Queue<byte[]> _receivedQueue;
/**
* Creates a hid bridge to the dongle. Should be created once.
* #param context is the UI context of Android.
* #param productId of the device.
* #param vendorId of the device.
*/
public HidBridge(Context context, int productId, int vendorId) {
_context = context;
_productId = productId;
_vendorId = vendorId;
_receivedQueue = new LinkedList<byte[]>();
}
/**
* Searches for the device and opens it if successful
* #return true, if connection was successful
*/
public boolean OpenDevice() {
_usbManager = (UsbManager) _context.getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = _usbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
_usbDevice = null;
// Iterate all the available devices and find ours.
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next();
if (device.getProductId() == _productId && device.getVendorId() == _vendorId) {
_usbDevice = device;
_deviceName = _usbDevice.getDeviceName();
}
}
if (_usbDevice == null) {
Log("Cannot find the device. Did you forgot to plug it?");
Log(String.format("\t I search for VendorId: %s and ProductId: %s", _vendorId, _productId));
return false;
}
// Create and intent and request a permission.
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(_context, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
_context.registerReceiver(mUsbReceiver, filter);
_usbManager.requestPermission(_usbDevice, mPermissionIntent);
Log("Found the device");
return true;
}
/**
* Closes the reading thread of the device.
*/
public void CloseTheDevice() {
try
{
StopReadingThread();
_context.unregisterReceiver(mUsbReceiver);
}
catch(RuntimeException e)
{
Log("Error happend while closing device. Usb reciver not connected.");
}
}
/**
* Starts the thread that continuously reads the data from the device.
* Should be called in order to be able to talk with the device.
*/
public void StartReadingThread() {
if (_readingThread == null) {
_runReadingThread = true;
_readingThread = new Thread(readerReceiver);
_readingThread.start();
} else {
Log("Reading thread already started");
}
}
/**
* Stops the thread that continuously reads the data from the device.
* If it is stopped - talking to the device would be impossible.
*/
public void StopReadingThread() {
if (_readingThread != null) {
// Just kill the thread. It is better to do that fast if we need that asap.
_runReadingThread = false;
_readingThread = null;
} else {
Log("No reading thread to stop");
}
}
/**
* Write data to the usb hid. Data is written as-is, so calling method is responsible for adding header data.
* #param bytes is the data to be written.
* #return true if succeed.
*/
public boolean WriteData(byte[] bytes) {
try
{
// Lock that is common for read/write methods.
synchronized (_locker) {
UsbInterface writeIntf = _usbDevice.getInterface(0);
UsbEndpoint writeEp = writeIntf.getEndpoint(1);
UsbDeviceConnection writeConnection = _usbManager.openDevice(_usbDevice);
// Lock the usb interface.
writeConnection.claimInterface(writeIntf, true);
// Write the data as a bulk transfer with defined data length.
int r = writeConnection.bulkTransfer(writeEp, bytes, bytes.length, 0);
if (r != -1) {
Log(String.format("Written %s bytes to the dongle. Data written: %s", r, composeString(bytes)));
} else {
Log("Error happened while writing data. No ACK");
}
// Release the usb interface.
writeConnection.releaseInterface(writeIntf);
writeConnection.close();
}
} catch(NullPointerException e)
{
Log("Error happend while writing. Could not connect to the device or interface is busy?");
Log.e("HidBridge", Log.getStackTraceString(e));
return false;
}
return true;
}
/**
* #return true if there are any data in the queue to be read.
*/
public boolean IsThereAnyReceivedData() {
synchronized(_locker) {
return !_receivedQueue.isEmpty();
}
}
/**
* Queue the data from the read queue.
* #return queued data.
*/
public byte[] GetReceivedDataFromQueue() {
synchronized(_locker) {
return _receivedQueue.poll();
}
}
// The thread that continuously receives data from the dongle and put it to the queue.
private Runnable readerReceiver = new Runnable() {
public void run() {
if (_usbDevice == null) {
Log("No device to read from");
return;
}
UsbEndpoint readEp;
UsbDeviceConnection readConnection = null;
UsbInterface readIntf = null;
boolean readerStartedMsgWasShown = false;
// We will continuously ask for the data from the device and store it in the queue.
while (_runReadingThread) {
// Lock that is common for read/write methods.
synchronized (_locker) {
try
{
if (_usbDevice == null) {
OpenDevice();
Log("No device. Recheking in 10 sec...");
Sleep(10000);
continue;
}
readIntf = _usbDevice.getInterface(0);
readEp = readIntf.getEndpoint(0);
if (!_usbManager.getDeviceList().containsKey(_deviceName)) {
Log("Failed to connect to the device. Retrying to acquire it.");
OpenDevice();
if (!_usbManager.getDeviceList().containsKey(_deviceName)) {
Log("No device. Recheking in 10 sec...");
Sleep(10000);
continue;
}
}
try
{
readConnection = _usbManager.openDevice(_usbDevice);
if (readConnection == null) {
Log("Cannot start reader because the user didn't gave me permissions or the device is not present. Retrying in 2 sec...");
Sleep(2000);
continue;
}
// Claim and lock the interface in the android system.
readConnection.claimInterface(readIntf, true);
}
catch (SecurityException e) {
Log("Cannot start reader because the user didn't gave me permissions. Retrying in 2 sec...");
Sleep(2000);
continue;
}
// Show the reader started message once.
if (!readerStartedMsgWasShown) {
Log("!!! Reader was started !!!");
readerStartedMsgWasShown = true;
}
// Read the data as a bulk transfer with the size = MaxPacketSize
int packetSize = readEp.getMaxPacketSize();
byte[] bytes = new byte[packetSize];
int r = readConnection.bulkTransfer(readEp, bytes, packetSize, 50);
if (r >= 0) {
byte[] trancatedBytes = new byte[r - 1]; // Truncate bytes in the honor of r
int i=0;
for (byte b : bytes) {
if (i > 0)
trancatedBytes[i - 1] = b;
i++;
}
_receivedQueue.add(trancatedBytes); // Store received data
Log(String.format("Message received of lengths %s and content: %s", r, composeString(bytes)));
}
// Release the interface lock.
readConnection.releaseInterface(readIntf);
readConnection.close();
}
catch (NullPointerException e) {
Log("Error happened while reading. No device or the connection is busy");
Log.e("HidBridge", Log.getStackTraceString(e));
}
catch (ThreadDeath e) {
if (readConnection != null) {
readConnection.releaseInterface(readIntf);
readConnection.close();
}
throw e;
}
}
// Sleep for 10 ms to pause, so other thread can write data or anything.
// As both read and write data methods lock each other - they cannot be run in parallel.
// Looks like Android is not so smart in planning the threads, so we need to give it a small time
// to switch the thread context.
Sleep(10);
}
}
};
private void Sleep(int milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
//call method to set up device communication
}
}
else {
Log.d("TAG", "permission denied for the device " + device);
}
}
}
}
};
/**
* Logs the message from HidBridge.
* #param message to log.
*/
private void Log(String message) {
//LogHandler logHandler = LogHandler.getInstance();
//logHandler.WriteMessage("HidBridge: " + message, LogHandler.GetNormalColor());
Log.i("HidBridge: ", message);
}
/**
* Composes a string from byte array.
*/
private String composeString(byte[] bytes) {
StringBuilder builder = new StringBuilder();
for (byte b: bytes) {
builder.append(b);
builder.append(" ");
}
return builder.toString();
}
}
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?
I am trying to turn add a wifi network programmatically and to connect to that network.
My code works fine if the wi-fi is already turned on.
If wi-fi is off, what i see wifimanager.addtonetwork() fails and when i see the wifi settings for the phone, i can see the status as scanning
If i try to connect again it works fine.
Please see code below.
Please help
private int changeNetwork(NetworkSetting setting) {
// If the SSID is empty, throw an error and return
if (setting.getSsid() == null || setting.getSsid().length() == 0) {
return doError(R.string.wifi_ssid_missing);
}
// If the network type is invalid
if (setting.getNetworkType() == NetworkType.NETWORK_INVALID) {
return doError(R.string.wifi_type_incorrect);
}
// If the password is empty, this is an unencrypted network
if (setting.getPassword() == null
|| setting.getPassword().length() == 0
|| setting.getNetworkType() == null
|| setting.getNetworkType() == NetworkType.NETWORK_NOPASS) {
return changeNetworkUnEncrypted(setting);
}
if (setting.getNetworkType() == NetworkType.NETWORK_WPA) {
return changeNetworkWPA(setting);
} else {
return changeNetworkWEP(setting);
}
}
private int doError(int resource_string) {
statusView.setText(resource_string);
// Give up on the connection
wifiManager.disconnect();
if (networkId > 0) {
wifiManager.removeNetwork(networkId);
networkId = -1;
}
if (receiverRegistered) {
unregisterReceiver(wifiReceiver);
receiverRegistered = false;
}
return -1;
}
private WifiConfiguration changeNetworkCommon(NetworkSetting input) {
statusView.setText(R.string.wifi_creating_network);
Log.d(TAG, "Adding new configuration: \nSSID: " + input.getSsid()
+ "\nType: " + input.getNetworkType());
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
// Android API insists that an ascii SSID must be quoted to be correctly
// handled.
config.SSID = NetworkUtil.convertToQuotedString(input.getSsid());
config.hiddenSSID = true;
return config;
}
private int requestNetworkChange(WifiConfiguration config) {
statusView.setText(R.string.wifi_changing_network);
return updateNetwork(config, false);
}
// Adding a WEP network
private int changeNetworkWEP(NetworkSetting input) {
WifiConfiguration config = changeNetworkCommon(input);
String pass = input.getPassword();
if (NetworkUtil.isHexWepKey(pass)) {
config.wepKeys[0] = pass;
} else {
config.wepKeys[0] = NetworkUtil.convertToQuotedString(pass);
}
config.allowedAuthAlgorithms
.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
return requestNetworkChange(config);
}
// Adding a WPA or WPA2 network
private int changeNetworkWPA(NetworkSetting input) {
WifiConfiguration config = changeNetworkCommon(input);
String pass = input.getPassword();
// Hex passwords that are 64 bits long are not to be quoted.
if (HEX_DIGITS_64.matcher(pass).matches()) {
Log.d(TAG, "A 64 bit hex password entered.");
config.preSharedKey = pass;
} else {
Log.d(TAG, "A normal password entered: I am quoting it.");
config.preSharedKey = NetworkUtil.convertToQuotedString(pass);
}
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
// For WPA
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
// For WPA2
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
return requestNetworkChange(config);
}
// Adding an open, unsecured network
private int changeNetworkUnEncrypted(NetworkSetting input) {
Log.d(TAG, "Empty password prompting a simple account setting");
WifiConfiguration config = changeNetworkCommon(input);
config.wepKeys[0] = "";
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
return requestNetworkChange(config);
}
/**
* If the given ssid name exists in the settings, then change its password
* to the one given here, and save
*
* #param ssid
*/
private WifiConfiguration findNetworkInExistingConfig(String ssid) {
List<WifiConfiguration> existingConfigs = wifiManager
.getConfiguredNetworks();
Log.i("Start comparing","Size "+existingConfigs.size() );
for (WifiConfiguration existingConfig : existingConfigs) {
Log.i("Compare with SSID", ssid + existingConfig.SSID);
if (existingConfig.SSID.equals(ssid)) {
Log.i("Compare success with SSID", ssid + existingConfig.SSID);
return existingConfig;
}
}
return null;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
/* if (intent == null
|| !intent.getAction().equals(Intents.WifiConnect.ACTION)) {
finish();
return;
} */
String ssid = intent.getStringExtra("ssid");
String password = intent.getStringExtra("password");
String networkType = intent.getStringExtra("type");
setContentView(R.layout.network);
statusView = (TextView) findViewById(R.id.networkStatus);
NetworkType networkT;
if ("WPA".equals(networkType)) {
networkT = NetworkType.NETWORK_WPA;
} else if ("WEP".equals(networkType)) {
networkT = NetworkType.NETWORK_WEP;
} else if ("nopass".equals(networkType)) {
networkT = NetworkType.NETWORK_NOPASS;
} else {
networkT = NetworkType.NETWORK_INVALID;
}
// This is not available before onCreate
wifiManager = (WifiManager) this.getSystemService(WIFI_SERVICE);
// Start WiFi, otherwise nothing will work
wifiManager.setWifiEnabled(true);
// So we know when the network changes
wifiReceiver = new WifiReceiver(wifiManager, this, statusView, ssid);
// The order matters!
mWifiStateFilter = new IntentFilter(
WifiManager.WIFI_STATE_CHANGED_ACTION);
mWifiStateFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mWifiStateFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mWifiStateFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
registerReceiver(wifiReceiver, mWifiStateFilter);
receiverRegistered = true;
if (password == null) {
password = "";
}
Log.d(TAG, "Adding new configuration: \nSSID: " + ssid + "Type: "
+ networkT);
NetworkSetting setting = new NetworkSetting(ssid, password, networkT);
changeNetwork(setting);
}
#Override
public void onPause() {
super.onPause();
if (receiverRegistered) {
unregisterReceiver(wifiReceiver);
receiverRegistered = false;
}
}
#Override
public void onResume() {
super.onResume();
if (wifiReceiver != null && mWifiStateFilter != null
&& !receiverRegistered) {
registerReceiver(wifiReceiver, mWifiStateFilter);
receiverRegistered = true;
}
}
#Override
protected void onDestroy() {
if (wifiReceiver != null) {
if (receiverRegistered) {
unregisterReceiver(wifiReceiver);
receiverRegistered = false;
}
wifiReceiver = null;
}
super.onDestroy();
}
/**
* Update the network: either create a new network or modify an existing
* network
*
* #param config
* the new network configuration
* #param disableOthers
* true if other networks must be disabled
* #return network ID of the connected network.
*/
private int updateNetwork(WifiConfiguration config, boolean disableOthers) {
WifiConfiguration found = findNetworkInExistingConfig(config.SSID);
wifiManager.disconnect();
if (found == null) {
Log.i("WIFI","SSID NOT FOUND");
statusView.setText(R.string.wifi_creating_network);
} else {
statusView.setText(R.string.wifi_modifying_network);
Log.d(TAG, "Removing network " + found.networkId);
wifiManager.removeNetwork(found.networkId);
wifiManager.saveConfiguration();
}
networkId = wifiManager.addNetwork(config);
Log.d(TAG, "Inserted/Modified network " + networkId);
if (networkId < 0) {
wifiManager.setWifiEnabled(true);
networkId = wifiManager.addNetwork(config);
Log.d(TAG, "Again Inserted/Modified network " + networkId);
return FAILURE_NO_NETWORK_ID;
}
// Try to disable the current network and start a new one.
if (!wifiManager.enableNetwork(networkId, disableOthers)) {
networkId = FAILURE_NO_NETWORK_ID;
return FAILURE_NO_NETWORK_ID;
}
errorCount = 0;
wifiManager.reassociate();
return networkId;
}
Here is my working code : ( its different from my previous code )
package com.idg.project.utils;
import java.util.List;
import java.util.regex.Pattern;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.util.Log;
/**
* #author Vikram Aggarwal
* #author Sean Owen
*/
public final class WifiConfigManager {
private static final String TAG = WifiConfigManager.class.getSimpleName();
private static final Pattern HEX_DIGITS = Pattern.compile("[0-9A-Fa-f]+");
private WifiConfigManager() {
}
public static void configure(final WifiManager wifiManager,
final String ssid,
final String password,
final String networkTypeString) {
Runnable configureRunnable = new Runnable() {
public void run() {
// Start WiFi, otherwise nothing will work
if (!wifiManager.isWifiEnabled()) {
Log.i(TAG, "Enabling wi-fi...");
if (wifiManager.setWifiEnabled(true)) {
Log.i(TAG, "Wi-fi enabled");
} else {
Log.w(TAG, "Wi-fi could not be enabled!");
return;
}
// This happens very quickly, but need to wait for it to enable. A little busy wait?
int count = 0;
while (!wifiManager.isWifiEnabled()) {
if (count >= 10) {
Log.i(TAG, "Took too long to enable wi-fi, quitting");
return;
}
Log.i(TAG, "Still waiting for wi-fi to enable...");
try {
Thread.sleep(1000L);
} catch (InterruptedException ie) {
// continue
}
count++;
}
}
NetworkType networkType = NetworkType.forIntentValue(networkTypeString);
if (networkType == NetworkType.NO_PASSWORD) {
changeNetworkUnEncrypted(wifiManager, ssid);
} else {
if (password == null || password.length() == 0) {
throw new IllegalArgumentException();
}
if (networkType == NetworkType.WEP) {
changeNetworkWEP(wifiManager, ssid, password);
} else if (networkType == NetworkType.WPA) {
changeNetworkWPA(wifiManager, ssid, password);
} }
}
};
new Thread(configureRunnable).start();
}
/**
* Update the network: either create a new network or modify an existing network
* #param config the new network configuration
* #return network ID of the connected network.
*/
private static void updateNetwork(WifiManager wifiManager, WifiConfiguration config) {
Integer foundNetworkID = findNetworkInExistingConfig(wifiManager, config.SSID);
if (foundNetworkID != null) {
Log.i(TAG, "Removing old configuration for network " + config.SSID);
wifiManager.removeNetwork(foundNetworkID);
wifiManager.saveConfiguration();
}
int networkId = wifiManager.addNetwork(config);
if (networkId >= 0) {
// Try to disable the current network and start a new one.
if (wifiManager.enableNetwork(networkId, true)) {
Log.i(TAG, "Associating to network " + config.SSID);
wifiManager.saveConfiguration();
} else {
Log.w(TAG, "Failed to enable network " + config.SSID);
}
} else {
Log.w(TAG, "Unable to add network " + config.SSID);
}
}
private static WifiConfiguration changeNetworkCommon(String ssid) {
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
// Android API insists that an ascii SSID must be quoted to be correctly handled.
config.SSID = quoteNonHex(ssid);
return config;
}
// Adding a WEP network
private static void changeNetworkWEP(WifiManager wifiManager, String ssid, String password) {
WifiConfiguration config = changeNetworkCommon(ssid);
config.wepKeys[0] = quoteNonHex(password, 10, 26, 58);
config.wepTxKeyIndex = 0;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
updateNetwork(wifiManager, config);
}
// Adding a WPA or WPA2 network
private static void changeNetworkWPA(WifiManager wifiManager, String ssid, String password) {
WifiConfiguration config = changeNetworkCommon(ssid);
// Hex passwords that are 64 bits long are not to be quoted.
config.preSharedKey = quoteNonHex(password, 64);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); // For WPA
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN); // For WPA2
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
updateNetwork(wifiManager, config);
}
// Adding an open, unsecured network
private static void changeNetworkUnEncrypted(WifiManager wifiManager, String ssid) {
WifiConfiguration config = changeNetworkCommon(ssid);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
updateNetwork(wifiManager, config);
}
private static Integer findNetworkInExistingConfig(WifiManager wifiManager, String ssid) {
List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
for (WifiConfiguration existingConfig : existingConfigs) {
if (existingConfig.SSID.equals(ssid)) {
return existingConfig.networkId;
}
}
return null;
}
private static String quoteNonHex(String value, int... allowedLengths) {
return isHexOfLength(value, allowedLengths) ? value : convertToQuotedString(value);
}
/**
* Encloses the incoming string inside double quotes, if it isn't already quoted.
* #param string the input string
* #return a quoted string, of the form "input". If the input string is null, it returns null
* as well.
*/
private static String convertToQuotedString(String string) {
if (string == null || string.length() == 0) {
return null;
}
// If already quoted, return as-is
if (string.charAt(0) == '"' && string.charAt(string.length() - 1) == '"') {
return string;
}
return '\"' + string + '\"';
}
/**
* #param value input to check
* #param allowedLengths allowed lengths, if any
* #return true if value is a non-null, non-empty string of hex digits, and if allowed lengths are given, has
* an allowed length
*/
private static boolean isHexOfLength(CharSequence value, int... allowedLengths) {
if (value == null || !HEX_DIGITS.matcher(value).matches()) {
return false;
}
if (allowedLengths.length == 0) {
return true;
}
for (int length : allowedLengths) {
if (value.length() == length) {
return true;
}
}
return false;
}
}
package com.idg.project.utils;
enum NetworkType {
WEP,
WPA,
NO_PASSWORD;
static NetworkType forIntentValue(String networkTypeString) {
if (networkTypeString == null) {
return NO_PASSWORD;
}
if ("WPA".equals(networkTypeString)) {
return WPA;
}
if ("WEP".equals(networkTypeString)) {
return WEP;
}
if ("nopass".equals(networkTypeString)) {
return NO_PASSWORD;
}
throw new IllegalArgumentException(networkTypeString);
}
}
You can turn on/off wifi using following instructions :
WifiManager wManager = (WifiManager)this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wManager.setWifiEnabled(booleanValue); //true or false
Set following permissions to your manifest:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE">
This code is deprecated in API 29 or over
public boolean setWifiEnabled (boolean enabled)
**This method was deprecated in API level 29**.
Starting with Build.VERSION_CODES#Q, applications are not allowed to
enable/disable Wi-Fi. Compatibility Note: For applications targeting
Build.VERSION_CODES.Q or above, this API will always return false and
will have no effect. If apps are targeting an older SDK (
Build.VERSION_CODES.P or below), they can continue to use this API.
Source : Link
You need to create wifiLock with WIFI_MODE_FULL_HIGH_PERF mode, based on the docs it will only work with the following constraints:
The lock is only active when the device is connected to an access point.
The lock is only active when the screen is on.
The lock is only active when the acquiring app is running in the foreground.