package com.owwlo.courier.s;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import com.owwlo.courier.db.CourierDatabaseHelper.ERROR_DETECT;
import com.owwlo.courier.s.Constants.SMS;
import com.owwlo.courier.s.poster.MessagePosterManager;
import com.owwlo.courier.s.utils.Utils;
public class CourierSService extends Service {
private static final String TAG = "CourierSService";
public static Context sContext;
private ServiceBinder mBinder = new ServiceBinder();
private Handler mHandler = new Handler();
private ContentObserver mObserver;
private Timer mBroadcastTimer = new Timer();
private TimerTask mBroadcastTimerTask = new TimerTask() {
@Override
public void run() {
if (mMessagePosterManager.isConnectedToHost()) {
mBroadcastTimer.cancel();
return;
}
(new BroadCastHelloMessage(BroadCastHelloMessage.TYPE_KNOCKDOOR))
.execute((Void[]) null);
}
};
private MessagePosterManager mMessagePosterManager;
private InetAddress mLastAddress = null;
private static String AUTH_CODE;
private List<AuthcodeListener> mAuthcodeListeners =
Collections.synchronizedList(new ArrayList<AuthcodeListener>());
public static final String EXTRA_ACTION = "extra_action";
public static final String BROADCAST_HELLO_MESSAGE = "broadcast_hello_message";
public CourierSService() {
Log.i(TAG, "class loaded.");
MessagePosterManager.init(this);
mMessagePosterManager = MessagePosterManager.getInstance();
}
private void addSMSObserver() {
sContext = getApplicationContext();
Log.i("CourierSService", "add a SMS observer. ");
ContentResolver localContentResolver = getContentResolver();
mObserver = new SMSObserver(getApplicationContext(),
localContentResolver, new SMSHandler(this));
localContentResolver.registerContentObserver(SMS.CONTENT_URI, true,
mObserver);
}
public final String getAuthCode() {
return AUTH_CODE;
}
public String getSystemTime() {
Time localTime = new Time();
localTime.setToNow();
return localTime.toString();
}
public IBinder onBind(Intent paramIntent) {
Log.i("CourierSService", "start IBinder~~~");
return mBinder;
}
public void onCreate() {
Log.i("CourierSService", "start onCreate~~~");
}
private boolean checkLastSession() {
Cursor cursor = sContext.getContentResolver().query(
Constants.URI_LAST_CONNECT, null, null, null,
ERROR_DETECT.CONNECT_TIME + " desc limit 1");
if (cursor.getCount() < 1) {
return false;
}
cursor.moveToFirst();
final String ip = cursor.getString(cursor
.getColumnIndex(ERROR_DETECT.IP));
sContext.getContentResolver().delete(Constants.URI_LAST_CONNECT, null,
null);
cursor.close();
if (mMessagePosterManager.isConnectedToHost()) {
mBroadcastTimer.cancel();
return false;
}
(new BroadCastHelloMessage(BroadCastHelloMessage.TYPE_RECONNECT, ip))
.execute((Void[]) null);
return true;
}
public boolean onUnbind(Intent paramIntent) {
return super.onUnbind(paramIntent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "On receive command.");
addSMSObserver();
if (Utils.isLocalNetConnected(this)
&& !mMessagePosterManager.isConnectedToHost()) {
if (mLastAddress == null
|| mLastAddress != getLocalIPAddress().get(0)) {
generateAuthCode();
}
boolean isConnectLost = checkLastSession();
try {
mBroadcastTimer.scheduleAtFixedRate(mBroadcastTimerTask,
isConnectLost ? Constants.RECONNECT_TRY_VALID_TIME : 0,
Constants.BROADCAST_DELAY_TIME);
} catch (Exception e) {
Log.i(TAG, "schedule failed.");
}
}
return Service.START_STICKY;
}
public void generateAuthCode() {
Log.i(TAG, "Generate AuthCode.");
InetAddress ip = getLocalIPAddress().get(0);
byte[] ipBytes = ip.getAddress();
String ipHex = Utils.byteArrayToHexString(ipBytes);
String ipPart = ipHex.substring(ipHex.length() - 2);
String imxi = Utils.getIMXI(sContext);
if (TextUtils.isEmpty(imxi) || imxi.length() <= 4) {
imxi = "d92c"; // Magic Code!
}
String imxiPart = imxi.substring(imxi.length() - 4, imxi.length() - 2);
AUTH_CODE = ipPart.substring(0, 1).toUpperCase()
+ Utils.generateRandomAuthChar()
+ ipPart.substring(1, 2).toUpperCase()
+ Utils.generateRandomAuthChar() + imxiPart;
Log.d(TAG, "AuthCode: " + AUTH_CODE);
Toast.makeText(getApplicationContext(), AUTH_CODE, Toast.LENGTH_SHORT)
.show();
for (AuthcodeListener al : mAuthcodeListeners) {
al.onAuthcodeChanged(getAuthCode());
}
}
public class ServiceBinder extends Binder {
public CourierSService getService() {
return CourierSService.this;
}
}
private class BroadCastHelloMessage extends AsyncTask<Void, Void, Void> {
public static final int TYPE_KNOCKDOOR = 0;
public static final int TYPE_RECONNECT = 1;
private JSONObject sendObj;
private String mIp;
@Override
protected Void doInBackground(Void... params) {
List<InetAddress> ipList = getLocalIPAddress();
broadcastMessage(ipList);
return null;
}
public BroadCastHelloMessage(int type) {
this(type, null);
}
public BroadCastHelloMessage(int type, String ip) {
if (type == TYPE_RECONNECT && ip == null) {
throw new InvalidParameterException();
}
mIp = ip;
switch (type) {
case TYPE_KNOCKDOOR:
sendObj = buildKnockDoorJSON();
break;
case TYPE_RECONNECT:
sendObj = buildReconnectJSON();
break;
}
}
private void broadcastMessage(List<InetAddress> ipList) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
socket.setBroadcast(true);
socket.setReuseAddress(true);
String message = Constants.COURIER_JSON_HEADER + sendObj;
byte[] data = message.getBytes("UTF-8");
Log.i(TAG, "Broadcast Message: " + message);
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
DatagramPacket packet = new DatagramPacket(base64.getBytes(),
base64.getBytes().length);
InetAddress broadcastAddr = InetAddress
.getByName("255.255.255.255");
packet.setAddress(broadcastAddr);
packet.setPort(Constants.BROADCAST_PORT);
socket.send(packet);
Log.d(TAG, "Hello Message Broadcasted.");
} catch (SocketException e) {
Log.d(TAG, "Broadcast socket failed.");
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
Log.d(TAG, "Broadcast packet sent failed.");
e.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
private JSONObject buildKnockDoorJSON() {
int port = mMessagePosterManager.getTcpListeningPort();
String imxi = Utils.getIMXI(sContext); // imxi could be ""
JSONObject json = new JSONObject();
try {
json.put(Constants.JSON_TYPE, Constants.JSON_TYPE_KNOCKDOOR);
json.put(Constants.JSON_PORT, port);
json.put(Constants.JSON_IMXI, imxi);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
private JSONObject buildReconnectJSON() {
int port = mMessagePosterManager.getTcpListeningPort();
String imxi = Utils.getIMXI(sContext); // imxi could be ""
JSONObject json = new JSONObject();
try {
json.put(Constants.JSON_TYPE, Constants.JSON_TYPE_RECONNECT);
// 这种情况下mIp不为空
json.put(Constants.JSON_RECONNECT_IP, mIp);
json.put(Constants.JSON_PORT, port);
json.put(Constants.JSON_IMXI, imxi);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}
private ArrayList<InetAddress> getLocalIPAddress() {
ArrayList<InetAddress> ipList = new ArrayList<InetAddress>();
try {
for (Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf
.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() // TODO 暂时只支持IPv4协议
&& inetAddress instanceof Inet4Address) {
ipList.add(inetAddress);
}
}
}
} catch (SocketException ex) {
Log.i(TAG, ex.toString());
}
return ipList;
}
public static interface AuthcodeListener {
public void onAuthcodeChanged(String newAuthcode);
}
public void addAuthcodeListener(AuthcodeListener al) {
mAuthcodeListeners.add(al);
}
public void removeAuthcodeListener(AuthcodeListener al) {
mAuthcodeListeners.remove(al);
}
}