/*
* Copyright 2012 Rui Araújo, Luís Fonseca
*
* This file is part of Router Keygen.
*
* Router Keygen is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Router Keygen is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Router Keygen. If not, see <http://www.gnu.org/licenses/>.
*/
package org.exobel.routerkeygen;
import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;
import com.farproc.wifi.connecter.Wifi;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.StandardExceptionParser;
import com.google.android.gms.analytics.Tracker;
import org.exobel.routerkeygen.AutoConnectManager.onConnectionListener;
import java.util.List;
public class AutoConnectService extends Service implements onConnectionListener {
public final static String SCAN_RESULT = "org.exobel.routerkeygen.SCAN_RESULT";
public final static String KEY_LIST = "org.exobel.routerkeygen.KEY_LIST";
private final static int DISCONNECT_WAITING_TIME = 10000;
private final static int FAILING_MINIMUM_TIME = 1500;
private final int UNIQUE_ID = R.string.app_name
+ AutoConnectService.class.getName().hashCode();
final private Binder mBinder = new LocalBinder();
private NotificationManager mNotificationManager;
private Handler handler;
private ScanResult network;
private List<String> keys;
private int attempts = 0;
private AutoConnectManager mReceiver;
private WifiManager wifi;
private int mNumOpenNetworksKept;
private int currentNetworkId = -1;
private boolean cancelNotification = true;
private long lastTimeDisconnected = -1;
private final Runnable tryAfterDisconnecting = new Runnable() {
public void run() {
tryingConnection();
}
};
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static PendingIntent getDefaultPendingIntent(Context context) {
final Intent i = new Intent(context, CancelOperationActivity.class)
.putExtra(CancelOperationActivity.SERVICE_TO_TERMINATE,
AutoConnectService.class.getName())
.putExtra(CancelOperationActivity.MESSAGE,
context.getString(R.string.cancel_auto_test))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB)
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
return PendingIntent.getActivity(context, 0, i,
PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@SuppressWarnings("deprecation")
public void onCreate() {
handler = new Handler();
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mReceiver = new AutoConnectManager(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
mNumOpenNetworksKept = Settings.Secure.getInt(getContentResolver(),
Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT, 10);
else
mNumOpenNetworksKept = Settings.Global.getInt(getContentResolver(),
Settings.Global.WIFI_NUM_OPEN_NETWORKS_KEPT, 10);
}
@Override
@SuppressWarnings("deprecation")
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
stopSelf();
return START_NOT_STICKY;
}
attempts = 0;
currentNetworkId = -1;
network = intent.getParcelableExtra(SCAN_RESULT);
keys = intent.getStringArrayListExtra(KEY_LIST);
final ConnectivityManager connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
final NetworkInfo mWifi = connManager
.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWifi.isConnected()) {
if (wifi.disconnect()) {
// besides disconnecting, we clean any previous configuration
Wifi.cleanPreviousConfiguration(wifi, network,
network.capabilities);
mNotificationManager
.notify(UNIQUE_ID,
NotificationUtils
.createProgressBar(
this,
getString(R.string.app_name),
getString(R.string.not_auto_connect_waiting),
keys.size(),
0,
false,
getDefaultPendingIntent(getApplicationContext())));
handler.postDelayed(tryAfterDisconnecting,
DISCONNECT_WAITING_TIME);
cancelNotification = true;
} else {
mNotificationManager.notify(
UNIQUE_ID,
NotificationUtils.getSimple(this,
getString(R.string.msg_error),
getString(R.string.msg_error_key_testing))
.build());
cancelNotification = false;
stopSelf();
return START_NOT_STICKY;
}
} else {
Wifi.cleanPreviousConfiguration(wifi, network, network.capabilities);
tryingConnection();
}
return START_STICKY;
}
private void tryingConnection() {
currentNetworkId = -1;
try {
currentNetworkId = Wifi.connectToNewNetwork(this, wifi, network,
keys.get(attempts++), mNumOpenNetworksKept);
Log.d(AutoConnectManager.class.getSimpleName(),
"Trying " + keys.get(attempts - 1));
if (currentNetworkId != -1) {
lastTimeDisconnected = System.currentTimeMillis();
if (attempts == 1)// first try, we register the listener
registerReceiver(mReceiver, new IntentFilter(
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
mNotificationManager
.notify(UNIQUE_ID,
NotificationUtils
.createProgressBar(
this,
getString(R.string.app_name),
getString(
R.string.not_auto_connect_key_testing,
keys.get(attempts - 1)),
keys.size(),
attempts,
false,
getDefaultPendingIntent(getApplicationContext())));
cancelNotification = true;
}
} catch (Exception e) {
e.printStackTrace();
// Get tracker.
Tracker t = ((RouterKeygenApplication) getApplication()).getTracker();
t.send(new HitBuilders.ExceptionBuilder()
.setDescription(
new StandardExceptionParser(this, null)
.getDescription(Thread.currentThread().getName(), e))
.setFatal(false)
.build()
);
}
if (currentNetworkId == -1) {
mNotificationManager.notify(
UNIQUE_ID,
NotificationUtils.getSimple(this,
getString(R.string.msg_error),
getString(R.string.msg_error_key_testing)).build());
cancelNotification = false;
stopSelf();
}
}
@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(tryAfterDisconnecting);
if (cancelNotification)
mNotificationManager.cancel(UNIQUE_ID);
reenableAllHotspots();
try {
unregisterReceiver(mReceiver);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailedConnection() {
/* Some phone are very strange and report multiples failures */
if ((System.currentTimeMillis() - lastTimeDisconnected) < FAILING_MINIMUM_TIME) {
Log.d(AutoConnectManager.class.getSimpleName(), "Ignoring signal");
return;
}
lastTimeDisconnected = System.currentTimeMillis();
wifi.removeNetwork(currentNetworkId);
if (attempts >= keys.size()) {
reenableAllHotspots();
mNotificationManager.notify(
UNIQUE_ID,
NotificationUtils.getSimple(this,
getString(R.string.msg_error),
getString(R.string.msg_no_correct_keys)).build());
cancelNotification = false;
stopSelf();
return;
}
tryingConnection();
}
@Override
public void onSuccessfulConection() {
reenableAllHotspots();
mNotificationManager.notify(
UNIQUE_ID,
NotificationUtils.getSimple(
this,
getString(R.string.app_name),
getString(R.string.not_correct_key_testing,
keys.get(attempts - 1))).build());
cancelNotification = false;
stopSelf();
}
private void reenableAllHotspots() {
final List<WifiConfiguration> configurations = wifi
.getConfiguredNetworks();
if (configurations != null) {
for (final WifiConfiguration config : configurations) {
wifi.enableNetwork(config.networkId, false);
}
}
}
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
private class LocalBinder extends Binder {
AutoConnectService getService() {
return AutoConnectService.this;
}
}
}