I have the code to display the RSSI value of a connected network. A background process continually calls getRssi and the loop time is a few milliseconds.
In the loop I post values by a handler to the UI thread where I display the current time, along with the loop rate.
However, the UI display updates every few seconds whereas the loop rate is a few milliseconds. Why the discrepancy?
package uk.co.moonsit.apps.wifi;
import uk.co.moonsit.apps.sensors.R;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.Time;
import android.view.View;
import android.widget.TextView;
public class WifiRssiActivity extends Activity {
private WifiManager wifi;
private Handler handler = new Handler();
// private String toastText;
private TextView tvStrength;
private TextView tvSpeed;
private TextView tvSSID;
private TextView tvTime;
private View view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wifi_rssi);
tvStrength = (TextView) findViewById(R.id.textViewWifiRssi);
tvSpeed = (TextView) findViewById(R.id.textViewSpeed);
tvSSID = (TextView) findViewById(R.id.textViewSSID);
tvTime = (TextView) findViewById(R.id.textViewTime);
String service = Context.WIFI_SERVICE;
wifi = (WifiManager) getSystemService(service);
Thread thread = new Thread(null, doBackgroundThreadProcessing,
"Background");
thread.start();
view = this.getWindow().getDecorView();
view.setKeepScreenOn(true);
}
private Runnable doBackgroundThreadProcessing = new Runnable() {
public void run() {
backgroundThreadProcessing();
}
};
private boolean isRunning = true;
// Method which does some processing in the background.
private void backgroundThreadProcessing() {
long mark = System.currentTimeMillis();
while (isRunning) {
int strength = 0;
int speed = 0;
String units = null;
String ssid = null;
long ms = 0;
WifiInfo info = wifi.getConnectionInfo();
if (info.getBSSID() != null) {
strength = WifiManager
.calculateSignalLevel(info.getRssi(), 100);
speed = info.getLinkSpeed();
units = WifiInfo.LINK_SPEED_UNITS;
ssid = info.getSSID();
}
long now = System.currentTimeMillis();
ms = now - mark;
mark = now;
GUIRunnable doUpdateGUI = new GUIRunnable(strength, speed, units,
ssid, ms);
handler.post(doUpdateGUI);
}
}
public class GUIRunnable implements Runnable {
private int strength;
private int speed;
private String units;
private String ssid = null;
private long ms;
public GUIRunnable(int st, int sp, String u, String ss, long m) {
strength = st;
speed = sp;
units = u;
ssid = ss;
ms = m;
}
public void run() {
updateGUI(strength, speed, units, ssid, ms);
}
}
private void updateGUI(int strength, int speed, String units, String ssid,
long ms) {
Time today = new Time(Time.getCurrentTimezone());
today.setToNow();
String millis = String.valueOf(System.currentTimeMillis());
String time = today.format("%k:%M:%S.")
+ millis.substring(millis.length() - 3);
tvStrength.setText("" + strength);
tvTime.setText(time + " - " + ms);
if (speed > 0)
tvSpeed.setText("" + speed + units);
if (ssid == null || ssid.length() == 0)
tvSSID.setText("No wifi connection");
else
tvSSID.setText(ssid);
// Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_LONG)
// .show();
}
#Override
protected void onResume() {
super.onResume();
isRunning = true;
}
#Override
protected void onPause() {
super.onPause();
// ssid = null;
isRunning = false;
}
}
checked google sources and it appears to me Android will only poll WIFI RSSI once every 3 seconds (most likely to save CPU and battery resources).
Here is the snippet from WifiStateMachine.java:-
/**
* Interval in milliseconds between polling for RSSI
* and linkspeed information
*/
private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
The android api doesn't specify the rate at which RSSI is updated or when it is updated, but in general I doubt it is faster than once a second for power saving purposes.
I found an article on how to use a broadcast receiver to get RSSI updates (rather than continuously polling): http://android-er.blogspot.com/2011/01/check-rssi-by-monitoring-of.html
The API does suggest that getRSSI and other methods are simply returning data from the last scan, so likely the update rate is the wifi scan interval. This is typically controlled by the build.prop file (see line 103 of this example file: http://androidforums.com/threads/stock-build-prop-download.561012/) and could vary from device to device so likely, the most efficient way to update the user on RSSI (or other wifi parameters) is to use a broadcast receiver and update when available.
Note, based on the answers to this question, some wifi device drivers don't support being configured by the build.prop file, so even if one had root access to modify the file, it doesn't guarantee that you can change the scan interval: https://android.stackexchange.com/questions/37621/where-can-i-find-settings-for-wifi-internal-scan-period
Related
I want to connect with VPN server but I don't want to use secret key. Currently the code snippet i found to programmatically create vpn connection is as follows:
MyVpnClient:
package com.example.android.toyvpn;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.VpnService;
import android.os.Bundle;
import android.widget.TextView;
public class ToyVpnClient extends Activity {
public interface Prefs {
String NAME = "connection";
String SERVER_ADDRESS = "server.address";
String SERVER_PORT = "server.port";
String SHARED_SECRET = "shared.secret";
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.form);
final TextView serverAddress = (TextView) findViewById(R.id.address);
final TextView serverPort = (TextView) findViewById(R.id.port);
final TextView sharedSecret = (TextView) findViewById(R.id.secret);
final SharedPreferences prefs = getSharedPreferences(Prefs.NAME, MODE_PRIVATE);
serverAddress.setText(prefs.getString(Prefs.SERVER_ADDRESS, ""));
serverPort.setText(prefs.getString(Prefs.SERVER_PORT, ""));
sharedSecret.setText(prefs.getString(Prefs.SHARED_SECRET, ""));
findViewById(R.id.connect).setOnClickListener(v -> {
prefs.edit()
.putString(Prefs.SERVER_ADDRESS, serverAddress.getText().toString())
.putString(Prefs.SERVER_PORT, serverPort.getText().toString())
.putString(Prefs.SHARED_SECRET, sharedSecret.getText().toString())
.commit();
Intent intent = VpnService.prepare(ToyVpnClient.this);
if (intent != null) {
startActivityForResult(intent, 0);
} else {
onActivityResult(0, RESULT_OK, null);
}
});
findViewById(R.id.disconnect).setOnClickListener(v -> {
startService(getServiceIntent().setAction(ToyVpnService.ACTION_DISCONNECT));
});
}
#Override
protected void onActivityResult(int request, int result, Intent data) {
if (result == RESULT_OK) {
startService(getServiceIntent().setAction(ToyVpnService.ACTION_CONNECT));
}
}
private Intent getServiceIntent() {
return new Intent(this, ToyVpnService.class);
}
}
MyVpnConnection:
package com.example.android.toyvpn;
import static java.nio.charset.StandardCharsets.US_ASCII;
import android.app.PendingIntent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.concurrent.TimeUnit;
public class ToyVpnConnection implements Runnable {
/**
* Callback interface to let the {#link ToyVpnService} know about new connections
* and update the foreground notification with connection status.
*/
public interface OnEstablishListener {
void onEstablish(ParcelFileDescriptor tunInterface);
}
/** Maximum packet size is constrained by the MTU, which is given as a signed short. */
private static final int MAX_PACKET_SIZE = Short.MAX_VALUE;
/** Time to wait in between losing the connection and retrying. */
private static final long RECONNECT_WAIT_MS = TimeUnit.SECONDS.toMillis(3);
/** Time between keepalives if there is no traffic at the moment.
*
* TODO: don't do this; it's much better to let the connection die and then reconnect when
* necessary instead of keeping the network hardware up for hours on end in between.
**/
private static final long KEEPALIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15);
/** Time to wait without receiving any response before assuming the server is gone. */
private static final long RECEIVE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(20);
/**
* Time between polling the VPN interface for new traffic, since it's non-blocking.
*
* TODO: really don't do this; a blocking read on another thread is much cleaner.
*/
private static final long IDLE_INTERVAL_MS = TimeUnit.MILLISECONDS.toMillis(100);
/**
* Number of periods of length {#IDLE_INTERVAL_MS} to wait before declaring the handshake a
* complete and abject failure.
*
* TODO: use a higher-level protocol; hand-rolling is a fun but pointless exercise.
*/
private static final int MAX_HANDSHAKE_ATTEMPTS = 50;
private final VpnService mService;
private final int mConnectionId;
private final String mServerName;
private final int mServerPort;
private final byte[] mSharedSecret;
private PendingIntent mConfigureIntent;
private OnEstablishListener mOnEstablishListener;
public ToyVpnConnection(final VpnService service, final int connectionId,
final String serverName, final int serverPort, final byte[] sharedSecret) {
mService = service;
mConnectionId = connectionId;
mServerName = serverName;
mServerPort= serverPort;
mSharedSecret = sharedSecret;
}
/**
* Optionally, set an intent to configure the VPN. This is {#code null} by default.
*/
public void setConfigureIntent(PendingIntent intent) {
mConfigureIntent = intent;
}
public void setOnEstablishListener(OnEstablishListener listener) {
mOnEstablishListener = listener;
}
#Override
public void run() {
try {
Log.i(getTag(), "Starting");
// If anything needs to be obtained using the network, get it now.
// This greatly reduces the complexity of seamless handover, which
// tries to recreate the tunnel without shutting down everything.
// In this demo, all we need to know is the server address.
final SocketAddress serverAddress = new InetSocketAddress(mServerName, mServerPort);
// We try to create the tunnel several times.
// TODO: The better way is to work with ConnectivityManager, trying only when the
// network is available.
// Here we just use a counter to keep things simple.
for (int attempt = 0; attempt < 10; ++attempt) {
// Reset the counter if we were connected.
if (run(serverAddress)) {
attempt = 0;
}
// Sleep for a while. This also checks if we got interrupted.
Thread.sleep(3000);
}
Log.i(getTag(), "Giving up");
} catch (IOException | InterruptedException | IllegalArgumentException e) {
Log.e(getTag(), "Connection failed, exiting", e);
}
}
private boolean run(SocketAddress server)
throws IOException, InterruptedException, IllegalArgumentException {
ParcelFileDescriptor iface = null;
boolean connected = false;
// Create a DatagramChannel as the VPN tunnel.
try (DatagramChannel tunnel = DatagramChannel.open()) {
// Protect the tunnel before connecting to avoid loopback.
if (!mService.protect(tunnel.socket())) {
throw new IllegalStateException("Cannot protect the tunnel");
}
// Connect to the server.
tunnel.connect(server);
// For simplicity, we use the same thread for both reading and
// writing. Here we put the tunnel into non-blocking mode.
tunnel.configureBlocking(false);
// Authenticate and configure the virtual network interface.
iface = handshake(tunnel);
// Now we are connected. Set the flag.
connected = true;
// Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(iface.getFileDescriptor());
// Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(iface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(MAX_PACKET_SIZE);
// Timeouts:
// - when data has not been sent in a while, send empty keepalive messages.
// - when data has not been received in a while, assume the connection is broken.
long lastSendTime = System.currentTimeMillis();
long lastReceiveTime = System.currentTimeMillis();
// We keep forwarding packets till something goes wrong.
while (true) {
// Assume that we did not make any progress in this iteration.
boolean idle = true;
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Write the outgoing packet to the tunnel.
packet.limit(length);
tunnel.write(packet);
packet.clear();
// There might be more outgoing packets.
idle = false;
lastReceiveTime = System.currentTimeMillis();
}
// Read the incoming packet from the tunnel.
length = tunnel.read(packet);
if (length > 0) {
// Ignore control messages, which start with zero.
if (packet.get(0) != 0) {
// Write the incoming packet to the output stream.
out.write(packet.array(), 0, length);
}
packet.clear();
// There might be more incoming packets.
idle = false;
lastSendTime = System.currentTimeMillis();
}
// If we are idle or waiting for the network, sleep for a
// fraction of time to avoid busy looping.
if (idle) {
Thread.sleep(IDLE_INTERVAL_MS);
final long timeNow = System.currentTimeMillis();
if (lastSendTime + KEEPALIVE_INTERVAL_MS <= timeNow) {
// We are receiving for a long time but not sending.
// Send empty control messages.
packet.put((byte) 0).limit(1);
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
lastSendTime = timeNow;
} else if (lastReceiveTime + RECEIVE_TIMEOUT_MS <= timeNow) {
// We are sending for a long time but not receiving.
throw new IllegalStateException("Timed out");
}
}
}
} catch (SocketException e) {
Log.e(getTag(), "Cannot use socket", e);
} finally {
if (iface != null) {
try {
iface.close();
} catch (IOException e) {
Log.e(getTag(), "Unable to close interface", e);
}
}
}
return connected;
}
private ParcelFileDescriptor handshake(DatagramChannel tunnel)
throws IOException, InterruptedException {
// To build a secured tunnel, we should perform mutual authentication
// and exchange session keys for encryption. To keep things simple in
// this demo, we just send the shared secret in plaintext and wait
// for the server to send the parameters.
// Allocate the buffer for handshaking. We have a hardcoded maximum
// handshake size of 1024 bytes, which should be enough for demo
// purposes.
ByteBuffer packet = ByteBuffer.allocate(1024);
// Control messages always start with zero.
packet.put((byte) 0).put(mSharedSecret).flip();
// Send the secret several times in case of packet loss.
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Wait for the parameters within a limited time.
for (int i = 0; i < MAX_HANDSHAKE_ATTEMPTS; ++i) {
Thread.sleep(IDLE_INTERVAL_MS);
// Normally we should not receive random packets. Check that the first
// byte is 0 as expected.
int length = tunnel.read(packet);
if (length > 0 && packet.get(0) == 0) {
return configure(new String(packet.array(), 1, length - 1, US_ASCII).trim());
}
}
throw new IOException("Timed out");
}
private ParcelFileDescriptor configure(String parameters) throws IllegalArgumentException {
// Configure a builder while parsing the parameters.
VpnService.Builder builder = mService.new Builder();
for (String parameter : parameters.split(" ")) {
String[] fields = parameter.split(",");
try {
switch (fields[0].charAt(0)) {
case 'm':
builder.setMtu(Short.parseShort(fields[1]));
break;
case 'a':
builder.addAddress(fields[1], Integer.parseInt(fields[2]));
break;
case 'r':
builder.addRoute(fields[1], Integer.parseInt(fields[2]));
break;
case 'd':
builder.addDnsServer(fields[1]);
break;
case 's':
builder.addSearchDomain(fields[1]);
break;
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Bad parameter: " + parameter);
}
}
// Create a new interface using the builder and save the parameters.
final ParcelFileDescriptor vpnInterface;
synchronized (mService) {
vpnInterface = builder
.setSession(mServerName)
.setConfigureIntent(mConfigureIntent)
.establish();
if (mOnEstablishListener != null) {
mOnEstablishListener.onEstablish(vpnInterface);
}
}
Log.i(getTag(), "New interface: " + vpnInterface + " (" + parameters + ")");
return vpnInterface;
}
private final String getTag() {
return ToyVpnConnection.class.getSimpleName() + "[" + mConnectionId + "]";
}
}
MyVpnService:
package com.example.android.toyvpn;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.VpnService;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class ToyVpnService extends VpnService implements Handler.Callback {
private static final String TAG = ToyVpnService.class.getSimpleName();
public static final String ACTION_CONNECT = "com.example.android.toyvpn.START";
public static final String ACTION_DISCONNECT = "com.example.android.toyvpn.STOP";
private Handler mHandler;
private static class Connection extends Pair<Thread, ParcelFileDescriptor> {
public Connection(Thread thread, ParcelFileDescriptor pfd) {
super(thread, pfd);
}
}
private final AtomicReference<Thread> mConnectingThread = new AtomicReference<>();
private final AtomicReference<Connection> mConnection = new AtomicReference<>();
private AtomicInteger mNextConnectionId = new AtomicInteger(1);
private PendingIntent mConfigureIntent;
#Override
public void onCreate() {
// The handler is only used to show messages.
if (mHandler == null) {
mHandler = new Handler(this);
}
// Create the intent to "configure" the connection (just start ToyVpnClient).
mConfigureIntent = PendingIntent.getActivity(this, 0, new Intent(this, ToyVpnClient.class),
PendingIntent.FLAG_UPDATE_CURRENT);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && ACTION_DISCONNECT.equals(intent.getAction())) {
disconnect();
return START_NOT_STICKY;
} else {
connect();
return START_STICKY;
}
}
#Override
public void onDestroy() {
disconnect();
}
#Override
public boolean handleMessage(Message message) {
Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
if (message.what != R.string.disconnected) {
updateForegroundNotification(message.what);
}
return true;
}
private void connect() {
// Become a foreground service. Background services can be VPN services too, but they can
// be killed by background check before getting a chance to receive onRevoke().
updateForegroundNotification(R.string.connecting);
mHandler.sendEmptyMessage(R.string.connecting);
// Extract information from the shared preferences.
final SharedPreferences prefs = getSharedPreferences(ToyVpnClient.Prefs.NAME, MODE_PRIVATE);
final String server = prefs.getString(ToyVpnClient.Prefs.SERVER_ADDRESS, "");
final byte[] secret = prefs.getString(ToyVpnClient.Prefs.SHARED_SECRET, "").getBytes();
final int port;
try {
port = Integer.parseInt(prefs.getString(ToyVpnClient.Prefs.SERVER_PORT, ""));
} catch (NumberFormatException e) {
Log.e(TAG, "Bad port: " + prefs.getString(ToyVpnClient.Prefs.SERVER_PORT, null), e);
return;
}
// Kick off a connection.
startConnection(new ToyVpnConnection(
this, mNextConnectionId.getAndIncrement(), server, port, secret));
}
private void startConnection(final ToyVpnConnection connection) {
// Replace any existing connecting thread with the new one.
final Thread thread = new Thread(connection, "ToyVpnThread");
setConnectingThread(thread);
// Handler to mark as connected once onEstablish is called.
connection.setConfigureIntent(mConfigureIntent);
connection.setOnEstablishListener(new ToyVpnConnection.OnEstablishListener() {
public void onEstablish(ParcelFileDescriptor tunInterface) {
mHandler.sendEmptyMessage(R.string.connected);
mConnectingThread.compareAndSet(thread, null);
setConnection(new Connection(thread, tunInterface));
}
});
thread.start();
}
private void setConnectingThread(final Thread thread) {
final Thread oldThread = mConnectingThread.getAndSet(thread);
if (oldThread != null) {
oldThread.interrupt();
}
}
private void setConnection(final Connection connection) {
final Connection oldConnection = mConnection.getAndSet(connection);
if (oldConnection != null) {
try {
oldConnection.first.interrupt();
oldConnection.second.close();
} catch (IOException e) {
Log.e(TAG, "Closing VPN interface", e);
}
}
}
private void disconnect() {
mHandler.sendEmptyMessage(R.string.disconnected);
setConnectingThread(null);
setConnection(null);
stopForeground(true);
}
private void updateForegroundNotification(final int message) {
startForeground(1, new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_vpn)
.setContentText(getString(message))
.setContentIntent(mConfigureIntent)
.build());
}
}
The above code is from the following url:
https://android.googlesource.com/platform/development/+/master/samples/ToyVpn
I have tried but I am not getting any workaround without secret key. Please help.
Unfortunately, we don't have any further requirements / spec of your VPN server.
You might want to have a look at the OpenVPN or strongSwan VPN (open source) implementations to see if they can fit your requirements. The latter for example provides the following ways of authentication on Android (https://wiki.strongswan.org/projects/strongswan/wiki/androidvpnclient):
Only IKEv2 is supported
Client authentication is limited to:
EAP authentication based on username/password (EAP-MSCHAPv2, EAP-MD5, EAP-GTC)
RSA/ECDSA authentication with private key/certificate
EAP-TLS with private key/certificate (see 1.4.5 for limitations)
I want to build an app that can connect with a single click of button which will create a vpn profile and connect to the vpn profile by parsig the username and password of the vpn server.
The code that is developed and modified is good to create and connect vpn but my vpn requires username and password to be entered so i want to do that programatically.
Here is my project
VpnClient.java
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.VpnService;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class VpnClient extends AppCompatActivity{
public interface Prefs {
String NAME = "connection";
String SERVER_ADDRESS = "server.address";
String SERVER_PORT = "server.port";
String SHARED_SECRET = "shared.secret";
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button disconnect = (Button) findViewById(R.id.disconnect);
Button connect = (Button) findViewById(R.id.connect);
// final TextView serverAddress = (TextView) findViewById(R.id.address);
//final TextView serverPort = (TextView) findViewById(R.id.port);
// final TextView sharedSecret = (TextView) findViewById(R.id.secret);
final SharedPreferences prefs = getSharedPreferences(Prefs.NAME, MODE_PRIVATE);
final String serverAddress = ""; !!I insert my vpnserver address in here which is my.vpnserver.com
final String serverPort = ""; !!As server port i am using 1723 for PPTP connection
connect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
prefs.edit()
.putString(Prefs.SERVER_ADDRESS,"")
.putString(Prefs.SERVER_PORT, "")
.putString(Prefs.SHARED_SECRET, "")
.commit();
Intent intent = VpnService.prepare(VpnClient.this);
if (intent != null) {
startActivityForResult(intent, 0);
} else {
onActivityResult(0, RESULT_OK, null);
}
}
});
disconnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startService(getServiceIntent().setAction(MyVpnService.ACTION_DISCONNECT));
}
});
}
#Override
protected void onActivityResult(int request, int result, Intent data) {
if (result == RESULT_OK) {
startService(getServiceIntent().setAction(MyVpnService.ACTION_CONNECT));
}
}
private Intent getServiceIntent() {
return new Intent(this, MyVpnService.class);
}
}
MyVpnService.java
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.support.annotation.RequiresApi;
import android.support.v4.util.Pair;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.LogRecord;
public class MyVpnService extends andenter code hereroid.net.VpnService implements android.os.Handler.Callback {
private static final String TAG = MyVpnService.class.getSimpleName();
public static final String ACTION_CONNECT = "com.yaksh.vpn.START";
public static final String ACTION_DISCONNECT = "com.yaksh.vpn.STOP";
private Handler mHandler;
private static class Connection extends Pair<Thread, ParcelFileDescriptor> {
public Connection(Thread thread, ParcelFileDescriptor pfd) {
super(thread, pfd);
}
}
private final AtomicReference<Thread> mConnectingThread = new AtomicReference<>();
private final AtomicReference<Connection> mConnection = new AtomicReference<>();
private AtomicInteger mNextConnectionId = new AtomicInteger(1);
private PendingIntent mConfigureIntent;
#Override
public void onCreate() {
// The handler is only used to show messages.
if (mHandler == null) {
mHandler = new Handler(this);
}
// Create the intent to "configure" the connection (just start ToyVpnClient).
mConfigureIntent = PendingIntent.getActivity(this, 0, new Intent(this, VpnClient.class),
PendingIntent.FLAG_UPDATE_CURRENT);
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && ACTION_DISCONNECT.equals(intent.getAction())) {
disconnect();
return START_NOT_STICKY;
} else {
connect();
return START_STICKY;
}
}
#Override
public void onDestroy() {
disconnect();
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
#Override
public boolean handleMessage(Message message) {
Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
if (message.what != R.string.disconnected) {
updateForegroundNotification(message.what);
}
return true;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private void connect() {
// Become a foreground service. Background services can be VPN services too, but they can
// be killed by background check before getting a chance to receive onRevoke().
updateForegroundNotification(R.string.connecting);
mHandler.sendEmptyMessage(R.string.connecting);
// Extract information from the shared preferences.
final SharedPreferences prefs = getSharedPreferences(VpnClient.Prefs.NAME, MODE_PRIVATE);
final String server = prefs.getString(VpnClient.Prefs.SERVER_ADDRESS, "");
final byte[] secret = prefs.getString(VpnClient.Prefs.SHARED_SECRET, "").getBytes();
final int port;
try {
port = Integer.parseInt(prefs.getString(VpnClient.Prefs.SERVER_PORT, ""));
} catch (NumberFormatException e) {
Log.e(TAG, "Bad port: " + prefs.getString(VpnClient.Prefs.SERVER_PORT, null), e);
return;
}
// Kick off a connection.
startConnection(new VpnConnection(
this, mNextConnectionId.getAndIncrement(), server, port, secret));
}
private void startConnection(final VpnConnection connection) {
// Replace any existing connecting thread with the new one.
final Thread thread = new Thread(connection, "ToyVpnThread");
setConnectingThread(thread);
// Handler to mark as connected once onEstablish is called.
connection.setConfigureIntent(mConfigureIntent);
connection.setOnEstablishListener(new VpnConnection.OnEstablishListener() {
public void onEstablish(ParcelFileDescriptor tunInterface) {
mHandler.sendEmptyMessage(R.string.connected);
mConnectingThread.compareAndSet(thread, null);
setConnection(new Connection(thread, tunInterface));
}
});
thread.start();
}
private void setConnectingThread(final Thread thread) {
final Thread oldThread = mConnectingThread.getAndSet(thread);
if (oldThread != null) {
oldThread.interrupt();
}
}
private void setConnection(final Connection connection) {
final Connection oldConnection = mConnection.getAndSet(connection);
if (oldConnection != null) {
try {
oldConnection.first.interrupt();
oldConnection.second.close();
} catch (IOException e) {
Log.e(TAG, "Closing VPN interface", e);
}
}
}
private void disconnect() {
mHandler.sendEmptyMessage(R.string.disconnected);
setConnectingThread(null);
setConnection(null);
stopForeground(true);
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private void updateForegroundNotification(final int message) {
startForeground(1, new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_vpn)
.setContentText(getString(message))
.setContentIntent(mConfigureIntent)
.build());
}
}
VpnConnection.java
import android.app.PendingIntent;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.support.annotation.RequiresApi;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.concurrent.TimeUnit;
/**
* Created by Yaksh on 11/3/2017.
*/
public class VpnConnection implements Runnable{
/**
* Callback interface to let the {#link MyVpnService} know about new connections
* and update the foreground notification with connection status.
*/
public interface OnEstablishListener {
void onEstablish(ParcelFileDescriptor tunInterface);
}
/** Maximum packet size is constrained by the MTU, which is given as a signed short. */
private static final int MAX_PACKET_SIZE = Short.MAX_VALUE;
/** Time to wait in between losing the connection and retrying. */
private static final long RECONNECT_WAIT_MS = TimeUnit.SECONDS.toMillis(3);
/** Time between keepalives if there is no traffic at the moment.
*
* TODO: don't do this; it's much better to let the connection die and then reconnect when
* necessary instead of keeping the network hardware up for hours on end in between.
**/
private static final long KEEPALIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15);
/** Time to wait without receiving any response before assuming the server is gone. */
private static final long RECEIVE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(20);
/**
* Time between polling the VPN interface for new traffic, since it's non-blocking.
*
* TODO: really don't do this; a blocking read on another thread is much cleaner.
*/
private static final long IDLE_INTERVAL_MS = TimeUnit.MILLISECONDS.toMillis(100);
/**
* Number of periods of length {#IDLE_INTERVAL_MS} to wait before declaring the handshake a
* complete and abject failure.
*
* TODO: use a higher-level protocol; hand-rolling is a fun but pointless exercise.
*/
private static final int MAX_HANDSHAKE_ATTEMPTS = 50;
private final MyVpnService mService;
private final int mConnectionId;
private final String mServerName;
private final int mServerPort;
private final byte[] mSharedSecret;
private PendingIntent mConfigureIntent;
private OnEstablishListener mOnEstablishListener;
public VpnConnection(final MyVpnService service, final int connectionId,
final String serverName, final int serverPort, final byte[] sharedSecret) {
mService = service;
mConnectionId = connectionId;
mServerName = serverName;
mServerPort= serverPort;
mSharedSecret = sharedSecret;
}
/**
* Optionally, set an intent to configure the VPN. This is {#code null} by default.
*/
public void setConfigureIntent(PendingIntent intent) {
mConfigureIntent = intent;
}
public void setOnEstablishListener(OnEstablishListener listener) {
mOnEstablishListener = listener;
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void run() {
try {
Log.i(getTag(), "Starting");
// If anything needs to be obtained using the network, get it now.
// This greatly reduces the complexity of seamless handover, which
// tries to recreate the tunnel without shutting down everything.
// In this demo, all we need to know is the server address.
final SocketAddress serverAddress = new InetSocketAddress(mServerName, mServerPort);
// We try to create the tunnel several times.
// network is available.
// Here we just use a counter to keep things simple.
for (int attempt = 0; attempt < 10; ++attempt) {
// Reset the counter if we were connected.
if (run(serverAddress)) {
attempt = 0;
}
// Sleep for a while. This also checks if we got interrupted.
Thread.sleep(3000);
}
Log.i(getTag(), "Giving up");
} catch (IOException | InterruptedException | IllegalArgumentException e) {
Log.e(getTag(), "Connection failed, exiting", e);
}
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
private boolean run(SocketAddress server)
throws IOException, InterruptedException, IllegalArgumentException {
ParcelFileDescriptor iface = null;
boolean connected = false;
// Create a DatagramChannel as the VPN tunnel.
try (DatagramChannel tunnel = DatagramChannel.open()) {
// Protect the tunnel before connecting to avoid loopback.
if (!mService.protect(tunnel.socket())) {
throw new IllegalStateException("Cannot protect the tunnel");
}
// Connect to the server.
tunnel.connect(server);
// For simplicity, we use the same thread for both reading and
// writing. Here we put the tunnel into non-blocking mode.
tunnel.configureBlocking(false);
// Authenticate and configure the virtual network interface.
iface = handshake(tunnel);
// Now we are connected. Set the flag.
connected = true;
// Packets to be sent are queued in this input stream.
FileInputStream in = new FileInputStream(iface.getFileDescriptor());
// Packets received need to be written to this output stream.
FileOutputStream out = new FileOutputStream(iface.getFileDescriptor());
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(MAX_PACKET_SIZE);
// Timeouts:
// - when data has not been sent in a while, send empty keepalive messages.
// - when data has not been received in a while, assume the connection is broken.
long lastSendTime = System.currentTimeMillis();
long lastReceiveTime = System.currentTimeMillis();
// We keep forwarding packets till something goes wrong.
while (true) {
// Assume that we did not make any progress in this iteration.
boolean idle = true;
// Read the outgoing packet from the input stream.
int length = in.read(packet.array());
if (length > 0) {
// Write the outgoing packet to the tunnel.
packet.limit(length);
tunnel.write(packet);
packet.clear();
// There might be more outgoing packets.
idle = false;
lastReceiveTime = System.currentTimeMillis();
}
// Read the incoming packet from the tunnel.
length = tunnel.read(packet);
if (length > 0) {
// Ignore control messages, which start with zero.
if (packet.get(0) != 0) {
// Write the incoming packet to the output stream.
out.write(packet.array(), 0, length);
}
packet.clear();
// There might be more incoming packets.
idle = false;
lastSendTime = System.currentTimeMillis();
}
// If we are idle or waiting for the network, sleep for a
// fraction of time to avoid busy looping.
if (idle) {
Thread.sleep(IDLE_INTERVAL_MS);
final long timeNow = System.currentTimeMillis();
if (lastSendTime + KEEPALIVE_INTERVAL_MS <= timeNow) {
// We are receiving for a long time but not sending.
// Send empty control messages.
packet.put((byte) 0).limit(1);
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
lastSendTime = timeNow;
} else if (lastReceiveTime + RECEIVE_TIMEOUT_MS <= timeNow) {
// We are sending for a long time but not receiving.
throw new IllegalStateException("Timed out");
}
}
}
} catch (SocketException e) {
Log.e(getTag(), "Cannot use socket", e);
} finally {
if (iface != null) {
try {
iface.close();
} catch (IOException e) {
Log.e(getTag(), "Unable to close interface", e);
}
}
}
return connected;
}
private ParcelFileDescriptor handshake(DatagramChannel tunnel)
throws IOException, InterruptedException {
// To build a secured tunnel, we should perform mutual authentication
// and exchange session keys for encryption. To keep things simple in
// this demo, we just send the shared secret in plaintext and wait
// for the server to send the parameters.
// Allocate the buffer for handshaking. We have a hardcoded maximum
// handshake size of 1024 bytes, which should be enough for demo
// purposes.
ByteBuffer packet = ByteBuffer.allocate(1024);
// Control messages always start with zero.
packet.put((byte) 0).put(mSharedSecret).flip();
// Send the secret several times in case of packet loss.
for (int i = 0; i < 3; ++i) {
packet.position(0);
tunnel.write(packet);
}
packet.clear();
// Wait for the parameters within a limited time.
for (int i = 0; i < MAX_HANDSHAKE_ATTEMPTS; ++i) {
Thread.sleep(IDLE_INTERVAL_MS);
// Normally we should not receive random packets. Check that the first
// byte is 0 as expected.
int length = tunnel.read(packet);
if (length > 0 && packet.get(0) == 0) {
return configure(new String(packet.array(), 1, length - 1).trim());
}
}
throw new IOException("Timed out");
}
private ParcelFileDescriptor configure(String parameters) throws IllegalArgumentException {
// Configure a builder while parsing the parameters.
MyVpnService.Builder builder = mService.new Builder();
for (String parameter : parameters.split(" ")) {
String[] fields = parameter.split(",");
try {
switch (fields[0].charAt(0)) {
case 'm':
builder.setMtu(Short.parseShort(fields[1]));
break;
case 'a':
builder.addAddress(fields[1], Integer.parseInt(fields[2]));
break;
case 'r':
builder.addRoute(fields[1], Integer.parseInt(fields[2]));
break;
case 'd':
builder.addDnsServer(fields[1]);
break;
case 's':
builder.addSearchDomain(fields[1]);
break;
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Bad parameter: " + parameter);
}
}
// Create a new interface using the builder and save the parameters.
final ParcelFileDescriptor vpnInterface;
synchronized (mService) {
vpnInterface = builder
.setSession(mServerName)
.setConfigureIntent(mConfigureIntent)
.establish();
if (mOnEstablishListener != null) {
mOnEstablishListener.onEstablish(vpnInterface);
}
}
Log.i(getTag(), "New interface: " + vpnInterface + " (" + parameters + ")");
return vpnInterface;
}
private final String getTag() {
return VpnConnection.class.getSimpleName() + "[" + mConnectionId + "]";
}
}
thank you for reading my problem, and hopefully I will get help from you guys.
I have LG watch urbane , and the OS is updated to 6.0.1
unfortunately after the update, I have many problems in the applications on my wear. is there any solution that I can rollback or downgrade the Android os to the previous one?
or could anyone help me with this code please?
I have an application that collects the motion data from Accelerometer and gyroscope wear sensors for five min.
I set the timestamp to 20hz per second (so I should get 20 samples per second from each sensor, thus 6000 samples(records) every 5 min). However, after 5 mins, I have got 30,000 samples( records) from Accelerometer sensor, and 6000 from gyroscope sensor, which is exactly what I want , but the problem with Accelerometer sensor. this problem didn't exist with the old OS of the wear. it appeared after the update. before the update of the Wear OS, I was getting the same number of samples from both sensors every five min (6000 records).
bellow is the code for the App
package edu.fordham.cis.wisdm.actipebble;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.Vibrator;
import android.support.wearable.view.WatchViewStub;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;
import java.util.*;
import java.lang.*;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
import com.google.common.collect.Lists;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Handles the collection of data and the transmission of the data back to the phone in batches.
* #author Andrew H. Johnston ajohnston9#fordham.edu
* #version 1.0STABLE
*/
public class WearTrainingActivity extends Activity implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mGyroscope;
private TextView mPrompt;
private TextView mProgress;
private GoogleApiClient googleApiClient;
private ArrayList<AccelerationRecord> mAccelerationRecords = new ArrayList<AccelerationRecord>();
private ArrayList<GyroscopeRecord> mGyroscopeRecords = new ArrayList<GyroscopeRecord>();
/**
* Tells the Sensor if it should save the records it
*/
private boolean shouldCollect = false;
/**
* Debugging tag
*/
private static final String TAG = "WearTrainingActivity";
/**
* One of the strategies to keep the watch screen on
*/
private PowerManager.WakeLock wakeLock;
/**
* Constant used to add clarity to formulae below
*/
private static final short MILLIS_IN_A_SECOND = 1000;
/**
* Change the second (i.e. 2nd) multiplicand to change the number of seconds of data collected
*/
private int delay = MILLIS_IN_A_SECOND * 300;
/**
* Expressed in a formula so that value makes more sense
*/
private int maxNumRecords = (delay / MILLIS_IN_A_SECOND) * 20;
/**
* Keeps track of the number of records obtained. This is more accurate than a time-based
* approach.
*/
private int recordCount = 0;
/**
* Sample rate, expressed as number of microseconds between samplings
*/
private static final int SAMPLE_RATE = 50000;
/**
* Maximum number of records we can send to the phone in one transmission.
*/
private static final int MAX_RECORDS_SENT_AT_ONCE = 3500;
/**
* Flag that signals the end of data transmission to the phone
*/
private static final String DATA_COLLECTION_DONE = "/thats-all-folks";
private static boolean first = true;
private static long first_time;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_training);
//Easy way to keep watch from sleeping on me
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mProgress = (TextView) findViewById(R.id.txtProgress);
mPrompt = (TextView) findViewById(R.id.txtPrompt);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub stub) {
mProgress = (TextView) findViewById(R.id.txtProgress);
mPrompt = (TextView) findViewById(R.id.txtPrompt);
}
});
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
//Collect at 20Hz (Once every 50,000 microseconds)
mSensorManager.registerListener(this, mAccelerometer, SAMPLE_RATE);
mSensorManager.registerListener(this, mGyroscope, SAMPLE_RATE);
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "Connected to phone.");
}
#Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "Connection to phone suspended. Code: " + i);
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "Fuck! Connection failed: " + connectionResult);
}
})
.addApi(Wearable.API)
.build();
googleApiClient.connect();
shouldCollect = true;
Log.d(TAG, "Started collecting");
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK,
"WearTrainingWakelock");
wakeLock.acquire();
}
#Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
#Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerometer, SAMPLE_RATE);
mSensorManager.registerListener(this, mGyroscope, SAMPLE_RATE);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (first) {
first_time = System.currentTimeMillis();
first = false;
}
if (shouldCollect) {
long temp1 = System.currentTimeMillis();
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
mAccelerationRecords.add(new AccelerationRecord(x,y,z,timestamp));
recordCount++;
break;
case Sensor.TYPE_GYROSCOPE:
GyroscopeRecord gyro = new GyroscopeRecord(x,y,z,timestamp);
//Clean up debugging output a little
if (recordCount % 10 == 0) {
Log.d(TAG, "Record is: " + gyro.toString());
}
mGyroscopeRecords.add(gyro);
}
//recordCount > maxNumRecords
if (shouldCollect && (System.currentTimeMillis() - first_time >= 5*60*1000)) {
shouldCollect = false;
new Thread(new SendDataToPhoneTask()).start();
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//Not used but must be overridden
}
class SendDataToPhoneTask implements Runnable {
#Override
public void run() {
Log.d(TAG, "Ending stream");
try {
List<List<AccelerationRecord>> accelLists =
Lists.partition(mAccelerationRecords, MAX_RECORDS_SENT_AT_ONCE);
List<List<GyroscopeRecord>> gyroLists =
Lists.partition(mGyroscopeRecords, MAX_RECORDS_SENT_AT_ONCE);
/* I know the following two for loops look like they could be
* abstracted into a single generic method, but due to type erasure
* of generics I can't do this with a single polymoprhic method.
*/
for (List<AccelerationRecord> list : accelLists) {
Log.d(TAG, "Sending list of acceleration records...");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
ArrayList<AccelerationRecord> tmp = new ArrayList<AccelerationRecord>(list);
Log.d(TAG, "List is of size: " + tmp.size());
oos.writeObject(tmp);
oos.flush();
oos.close();
byte[] data = baos.toByteArray();
PutDataMapRequest dataMapRequest = PutDataMapRequest.create("/accel-data");
dataMapRequest.getDataMap().putByteArray("/accel", data);
PutDataRequest request = dataMapRequest.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(googleApiClient, request);
}
//Having a bug where a "that's all" message actually beats the
//data to the phone, so we'll just attach the message to the last
//list of data going out
for (int i = 0; i < gyroLists.size(); ++i) {
List<GyroscopeRecord> list = gyroLists.get(i);
Log.d(TAG, "Sending list of gyroscope records...");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oosG = new ObjectOutputStream(baos);
ArrayList<GyroscopeRecord> tmp = new ArrayList<GyroscopeRecord>(list);
Log.d(TAG, "List is of size: " + tmp.size());
oosG.writeObject(tmp);
oosG.flush();
oosG.close();
byte[] data = baos.toByteArray();
PutDataMapRequest dataMapRequest = PutDataMapRequest.create("/gyro-data");
dataMapRequest.getDataMap().putByteArray("/gyro", data);
if ((i+1) == gyroLists.size()) {
dataMapRequest.getDataMap().putString("/done", DATA_COLLECTION_DONE);
} else {
dataMapRequest.getDataMap().putString("/done", "/not-done");
}
PutDataRequest request = dataMapRequest.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(googleApiClient, request);
}
//Vibrate and tell the user to check their phone
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
vibrator.vibrate(500L); //Vibrate for half a second
runOnUiThread(new Runnable() {
#Override
public void run() {
mPrompt.setText("Please finish the training by opening your phone.");
mProgress.setText("");
}
});
} catch (IOException e) {
Log.d(TAG, "Something fucky happened: " + e.getMessage());
} finally {
//Screen can turn off now.
wakeLock.release();
}
}
}
}
I need to create an app to scan and gather the list of access points available, including the signal strength. For some reason every time I initiate the WifiManager.startScan() method, it returns true, but my Broadcast Receiver is never called.
Here is the code for my class:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Manages radio (wifi) information
*/
public class NetworkManager {
private final Handler handler = new Handler();
private WifiReceiver receiverWifi;
private TextView mTextView;
// true if scan is done
private boolean mDone = true;
private WifiManager mManager;
List<ScanResult> results;
// Data in form SSID, RSSI
ArrayList<HashMap<String, String>> oldData = new ArrayList<>();
ArrayList<HashMap<String, String>> data = new ArrayList<>();
public NetworkManager(Context c, TextView mTextView) {
this.mTextView = mTextView;
mManager = (WifiManager) c.getSystemService(Context.WIFI_SERVICE);
if (!mManager.isWifiEnabled())
mManager.setWifiEnabled(true);
receiverWifi = new WifiReceiver();
c.registerReceiver(receiverWifi,
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
if (mManager.isWifiEnabled() == false)
mManager.setWifiEnabled(true);
scanAndSet();
Log.d("TAG", "Constructor after scan and set");
WifiInfo wifiInfo = mManager.getConnectionInfo();
Log.d("TAG", wifiInfo.toString());
}
/**
* Sets the SSID and RSSI from the scanned results into main data bin.
*/
// private void setNetworkData() {
// String result = "";
// oldData = data;
// data.clear();
// for(ScanResult r : results) {
// String ssid = r.SSID;
// String rssi = String.valueOf(r.level);
// HashMap<String, String> x = new HashMap<>();
// x.put(ssid, rssi);
// result += "ID: " + ssid + " - RSSI" + rssi + "/n";
// Log.d("TAG", "R: " + result);
// }
// mDone = true;
// }
public void scanAndSet() {
handler.post(new Runnable() {
#Override
public void run() {
Log.d("TAG", "Start: " + mManager.startScan());
}
});
}
public boolean isDone() {
return mDone;
}
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TAG", "On receive broadcast receiver");
ArrayList<String> connections=new ArrayList<String>();
ArrayList<Float> Signal_Strenth= new ArrayList<Float>();
List<ScanResult> wifiList = mManager.getScanResults();
for(int i = 0; i < wifiList.size(); i++) {
connections.add(wifiList.get(i).SSID);
}
}
}
}
the scanAndSet() method is suppose to initiate the scan. The startScan() method returns true. The receiver is only called when I go to the settings and the device searches for wifi networks, I can't seem to do it programatically.
Another peculiar thing is when I want to get information for the connected access point using WifiManager.getConnectionInfo().toString(), this is what it gives.
D/TAG: SSID: <unknown ssid>, BSSID: <none>, Supplicant state: COMPLETED, RSSI: -127, Link speed: -1Mbps, Frequency: -1MHz, Net ID: -1, Metered hint: false, score: 0
I'm completely lost, is this common with all Wear devices or is the wifi api disabled?
My understanding is,existing Screen OFF and ON intents are not exactly mean that the device is in sleep and waked up respectively. Any applications on the device holds partial wake lock, device will not be in deep sleep but screen may be off/on.
Is there any intents to listen CPU "WAKE UP" and "SLEEP" ?
Is there any way, we know CPU is waked UP from deep sleep ?
I needed a tool to do exactly this when troubleshooting some timing behavior on my app in the background. So I made my own class to do it. See code below. Here's how you use it:
CpuSleepDetector.getInstance().setSleepEndNotifier(new CpuSleepDetector.SleepEndNotifier() {
#Override
public void cpuSleepEnded(long sleepDurationMillis) {
Log.d(TAG, "The CPU just exited sleep. It was sleeping for "+sleepDurationMillis+" ms.");
}
});
CpuSleepDetector.getInstance().logDump();
The logDump method will dump the last 100 sleep events to LogCat. This is useful in troubleshooting, becaue to get the CPU to sleep, I had to not only disconnect my USB cable from my phone, I actually had to turn off my adb connection over WiFi. This way, you can reconnect adb at a later time and use the logDump method to get recent detections.
I know this is an old question, but hopefully this will be useful to somebody else.
Here's the code for the detector class:
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
public class CpuSleepDetector {
private static final String TAG = CpuSleepDetector.class.getSimpleName();
private static CpuSleepDetector instance = null;
private HandlerThread thread;
private Handler handler;
private SleepEndNotifier notifier;
public static CpuSleepDetector getInstance() {
if (instance == null) {
instance = new CpuSleepDetector();
}
return instance;
}
private CpuSleepDetector() {
thread = new HandlerThread("cpuSleepDetectorThread");
thread.start();
handler = new Handler(thread.getLooper());
watchForSleep();
}
private void watchForSleep(){
// uptime stalls when cpu stalls
final long uptimeAtStart = SystemClock.uptimeMillis();
final long realtimeAtStart = SystemClock.elapsedRealtime();
handler.postDelayed(new Runnable() {
#Override
public void run() {
long uptimeAtEnd = SystemClock.uptimeMillis();
long realtimeAtEnd = SystemClock.elapsedRealtime();
long realtimeDelta = realtimeAtEnd - realtimeAtStart;
long uptimeDelta = uptimeAtEnd - uptimeAtStart;
final long sleepTime = realtimeDelta - uptimeDelta;
if (sleepTime > 1) {
detectedStalls.put(new Date(), sleepTime);
prune();
if (notifier != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
notifier.cpuSleepEnded(sleepTime);
}
});
}
}
watchForSleep();
}
}, 1000);
}
private HashMap<Date,Long> detectedStalls = new HashMap<Date,Long>();
private HashMap<Date,Long> getDetectedStalls() {
return detectedStalls;
}
private void prune() {
int numberToPrune = detectedStalls.size() - 100;
if (numberToPrune > 0) {
HashMap<Date,Long> newDetectedStalls = new HashMap<Date,Long>();
ArrayList<Date> dates = new ArrayList<>(getDetectedStalls().keySet());
Collections.sort(dates);
for (int i = numberToPrune; i < detectedStalls.size(); i++) {
newDetectedStalls.put(dates.get(i), detectedStalls.get(dates.get(i)));
}
detectedStalls = newDetectedStalls;
}
}
public void logDump() {
Log.d(TAG, "Last 100 known CPU sleep incidents:");
ArrayList<Date> dates = new ArrayList<>(getDetectedStalls().keySet());
Collections.sort(dates);
for (Date date: dates) {
Log.d(TAG, ""+date+": "+getDetectedStalls().get(date));
}
}
public void setSleepEndNotifier(SleepEndNotifier notifier) {
this.notifier = notifier;
}
public interface SleepEndNotifier {
public void cpuSleepEnded(long sleepDurationMillis);
}
}