package com.githang.android.apnbb; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Handler; import android.os.HandlerThread; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import org.androidpn.client.LogUtil; import org.androidpn.client.NotificationIQ; import org.androidpn.client.NotificationIQProvider; import org.androidpn.client.XmppManager; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.PacketCollector; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Registration; import org.jivesoftware.smack.provider.ProviderManager; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; /** * XMLPP连接的控制类,控制连接、注册、登录、重连、响应连接状态及回执等。 * Created by msdx on 14-4-17. */ public class XmppConnectReceiver extends BroadcastReceiver { private static final String LOG_TAG = LogUtil.makeLogTag(XmppConnectReceiver.class); private static final Object lock = new Object(); private static XmppConnectReceiver instance = null; private Context context; private Handler handler; private XmppManager xmppManager; private SharedPreferences sharedPrefs; private String xmppHost; private int xmppPort; private Runnable disconnectTask; private Runnable reconnectTask; private Runnable loginServerTask; private boolean isConnecting; private XmppConnectReceiver(Context context, XmppManager xmppManager) { this.context = context; this.xmppManager = xmppManager; LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); IntentFilter filter = new IntentFilter(); filter.addAction(BroadcastUtil.APN_ACTION_LOGIN); filter.addAction(BroadcastUtil.APN_ACTION_RECONNECT); filter.addAction(BroadcastUtil.APN_ACTION_REQUEST_STATUS); filter.addAction(BroadcastUtil.APN_ACTION_RECEIPT); lbm.registerReceiver(this, filter); BroadcastUtil.sendBroadcast(context, BroadcastUtil.ANDROIDPN_MSG_RECEIVER_READY); sharedPrefs = context.getSharedPreferences(Constants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); xmppHost = sharedPrefs.getString(Constants.XMPP_HOST, "localhost"); xmppPort = sharedPrefs.getInt(Constants.XMPP_PORT, 5222); HandlerThread thread = new HandlerThread(XmppConnectReceiver.class.getSimpleName()); thread.start(); handler = new Handler(thread.getLooper()); disconnectTask = new DisconnectTask(); reconnectTask = new ReconnectTask(); loginServerTask = new LoginServer(); } public static final void initInstance(Context context, XmppManager xmppManager) { if (instance == null) { synchronized (lock) { if (instance == null) { instance = new XmppConnectReceiver(context, xmppManager); } } } } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(LOG_TAG, action); if (BroadcastUtil.APN_ACTION_LOGIN.equals(action)) { doLogin(); } else if (BroadcastUtil.APN_ACTION_RECONNECT.equals(action)) { doReconnect(); } else if (BroadcastUtil.APN_ACTION_DISCONNECT.equals(action)) { doDisconnect(); } else if (BroadcastUtil.APN_ACTION_REQUEST_STATUS.equals(action)) { if (xmppManager.isAuthenticated()) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_CONNECTED); } else if (isConnecting) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_CONNECTING); } else { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_DISCONNECT); } } else if (BroadcastUtil.APN_ACTION_RECEIPT.equals(action)) { doSendReceipt((IQ) intent.getSerializableExtra(Constants.INTENT_EXTRA_IQ)); } } private void doSendReceipt(IQ iq) { IQ result = IQ.createResultIQ(iq); xmppManager.getConnection().sendPacket(result); Log.d(LOG_TAG, "receipt" + iq.toString()); } private void doLogin() { if (xmppManager.isAuthenticated()) { return; } handler.removeCallbacks(loginServerTask); handler.post(loginServerTask); } private void doReconnect() { handler.post(reconnectTask); } private void doDisconnect() { handler.removeCallbacks(loginServerTask); handler.post(disconnectTask); } public static class DelayTime { private static final DelayTime delayTime = new DelayTime(); private AtomicInteger times = new AtomicInteger(0); public static void resetTimes() { delayTime.times.set(0); } public static void increase() { delayTime.times.incrementAndGet(); } public static int getWaitingTime() { int time = delayTime.times.get(); if (time == 0) { return 0; } // 20秒一次 if (time < 15) { return 20; } // 60秒一次 if (time < 20) { return 60; } return time < 30 ? 120 : 300; } } /** * A runnable task to connect the server. */ private class LoginServer implements Runnable { private static final int REGISTER_TIME_OUT = 60000; private LoginServer() { } public void run() { final String uuid = UUIDUtil.getID(context); // 密码也设成UUID,以使应用程序清除数据之后,再注册的用户username是一样的。 final String newUsername = uuid; final String newPassword = uuid; isConnecting = true; boolean isSuccess = false; if(connect()) { if(register(newUsername, newPassword)) { SharedPreferences.Editor editor = sharedPrefs.edit(); editor.putString(Constants.XMPP_USERNAME, newUsername); editor.putString(Constants.XMPP_PASSWORD, newPassword); editor.commit(); String username = sharedPrefs.getString(Constants.XMPP_USERNAME, ""); String password = sharedPrefs.getString(Constants.XMPP_PASSWORD, ""); if(login(username, password)) { isSuccess = true; BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_LOGINED); } else { } } } else { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_CONNECT_FAILED); } isConnecting = false; if(!isSuccess) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_ACTION_RECONNECT); } } private boolean connect() { Log.i(LOG_TAG, "ConnectTask.run()..."); if (!xmppManager.isConnected()) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_CONNECTING); // Create the configuration for this new connection ConnectionConfiguration connConfig = new ConnectionConfiguration( xmppHost, xmppPort); // connConfig.setSecurityMode(SecurityMode.disabled); connConfig.setSecurityMode(ConnectionConfiguration.SecurityMode.required); connConfig.setSASLAuthenticationEnabled(false); connConfig.setCompressionEnabled(false); XMPPConnection connection = new XMPPConnection(connConfig); xmppManager.setConnection(connection); try { // Connect to the server connection.connect(); BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_CONNECTED); Log.i(LOG_TAG, "XMPP connected successfully"); if (NotifierConfig.iqProvider == null) { ProviderManager.getInstance().addIQProvider(Constants.ELEMENT_NAME, Constants.DEFAULT_NAMESPACE, new NotificationIQProvider()); } else { try { ProviderManager.getInstance().addIQProvider(Constants.ELEMENT_NAME, Constants.DEFAULT_NAMESPACE, Class.forName(NotifierConfig.iqProvider).newInstance()); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); ProviderManager.getInstance().addIQProvider(Constants.ELEMENT_NAME, Constants.DEFAULT_NAMESPACE, new NotificationIQProvider()); } } return true; } catch (XMPPException e) { Log.e(LOG_TAG, "XMPP connection failed", e); return false; } } else { Log.i(LOG_TAG, "XMPP connected already"); return true; } } public boolean register(String user, String pass) { Log.i(LOG_TAG, "RegisterTask.run()..."); if (xmppManager.isRegistered()) { Log.i(LOG_TAG, "Account registered already"); return true; } final Registration registration = new Registration(); PacketFilter packetFilter = new AndFilter(new PacketIDFilter( registration.getPacketID()), new PacketTypeFilter( IQ.class)); PacketCollector collector = xmppManager.getConnection().createPacketCollector(packetFilter); registration.setType(IQ.Type.SET); registration.addAttribute("username", user); registration.addAttribute("password", pass); if (xmppManager.getConnection().isConnected()) { xmppManager.getConnection().sendPacket(registration); IQ result = (IQ) collector.nextResult(REGISTER_TIME_OUT); collector.cancel(); if(result == null) { Log.d(LOG_TAG, "The server didn't return result after 60 seconds."); return false; } else if (result.getType() == IQ.Type.ERROR) { if(result.getError().toString().contains("409")) { return true; } else { return false; } } else if (result.getType() == IQ.Type.RESULT) { return true; } return false; } else { Log.d(LOG_TAG, "connection is not connected"); return false; } } public boolean login(String user, String pass) { Log.i(LOG_TAG, "LoginTask.run()..."); if (xmppManager.isAuthenticated()) { Log.i(LOG_TAG, "Logged in already"); return true; } BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_LOGINING); try { xmppManager.getConnection().login( user, pass, XmppManager.XMPP_RESOURCE_NAME); Log.d(LOG_TAG, "Loggedn in successfully"); BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_LOGIN_SUCCESS); // connection listener if (xmppManager.getConnectionListener() != null) { xmppManager.getConnection().addConnectionListener( xmppManager.getConnectionListener()); } PacketFilter packetFilter = null; if (NotifierConfig.iq == null) { // packet filter packetFilter = new PacketTypeFilter( NotificationIQ.class); } else { packetFilter = new PacketTypeFilter(Class.forName(NotifierConfig.iq)); } // packet listener PacketListener packetListener = xmppManager .getPacketListener(); xmppManager.getConnection().addPacketListener(packetListener, packetFilter); xmppManager.getConnection().startKeepAliveThread(xmppManager); return true; } catch (XMPPException e) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_LOGIN_FAIL); Log.e(LOG_TAG, "LoginTask.run()... xmpp error"); Log.e(LOG_TAG, "Failed to login to xmpp server. Caused by: " + e.getMessage(), e); String INVALID_CREDENTIALS_ERROR_CODE = "401"; String errorMessage = e.getMessage(); if (errorMessage != null && errorMessage.contains(INVALID_CREDENTIALS_ERROR_CODE)) { xmppManager.reregisterAccount(); } return false; } catch (Exception e) { BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_LOGIN_FAIL); Log.e(LOG_TAG, "LoginTask.run()... other error"); Log.e(LOG_TAG, "Failed to login to xmpp server. Caused by: " + e.getMessage()); return false; } } } public class DisconnectTask implements Runnable { public void run() { if (xmppManager.isConnected()) { Log.d(LOG_TAG, "terminatePersistentConnection()... run()"); xmppManager.getConnection().removePacketListener( xmppManager.getPacketListener()); xmppManager.getConnection().disconnect(); } } } public class ReconnectTask implements Runnable { public void run() { if (xmppManager.isAuthenticated() || !NetworkUtil.isNetworkAvaible(context)) { handler.removeCallbacks(reconnectTask); DelayTime.resetTimes(); return; } if(isConnecting) { return; } Log.d(LOG_TAG, "reconnectTask..."); BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_STATUS_RECONNECTING); try { Thread.sleep(DelayTime.getWaitingTime() * 1000); } catch (InterruptedException e) { e.printStackTrace(); } BroadcastUtil.sendBroadcast(context, BroadcastUtil.APN_ACTION_LOGIN); DelayTime.increase(); } } }