block code when doing unforced disconnect

This commit is contained in:
Daniel Gultsch 2015-10-16 09:58:31 +02:00
parent c1716a35e3
commit fb7359e6a3
2 changed files with 220 additions and 223 deletions

View file

@ -26,10 +26,9 @@ import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.security.KeyChain; import android.security.KeyChain;
import android.security.KeyChainException; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import android.util.DisplayMetrics;
import android.util.Pair; import android.util.Pair;
import net.java.otr4j.OtrException; import net.java.otr4j.OtrException;
@ -38,16 +37,11 @@ import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionImpl;
import net.java.otr4j.session.SessionStatus; import net.java.otr4j.session.SessionStatus;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
@ -124,9 +118,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification"; public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
public static final String ACTION_TRY_AGAIN = "try_again"; public static final String ACTION_TRY_AGAIN = "try_again";
public static final String ACTION_DISABLE_ACCOUNT = "disable_account"; public static final String ACTION_DISABLE_ACCOUNT = "disable_account";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private final IqGenerator mIqGenerator = new IqGenerator(this);
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
public DatabaseBackend databaseBackend;
private ContentObserver contactObserver = new ContentObserver(null) { private ContentObserver contactObserver = new ContentObserver(null) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
@ -137,89 +138,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
startService(intent); startService(intent);
} }
}; };
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private final FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsImageDirectory()) {
@Override
public void onEvent(int event, String path) {
if (event == FileObserver.DELETE) {
markFileDeleted(path.split("\\.")[0]);
}
}
};
private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
public void onJinglePacketReceived(Account account, JinglePacket packet) {
mJingleConnectionManager.deliverPacket(account, packet);
}
};
private final OnBindListener mOnBindListener = new OnBindListener() {
@Override
public void onBind(final Account account) {
account.getRoster().clearPresences();
fetchRosterFromServer(account);
fetchBookmarks(account);
sendPresence(account);
connectMultiModeConversations(account);
mMessageArchiveService.executePendingQueries(account);
mJingleConnectionManager.cancelInTransmission();
syncDirtyContacts(account);
account.getAxolotlService().publishBundlesIfNeeded(true, false);
}
};
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
@Override
public void onMessageAcknowledged(Account account, String uuid) {
for (final Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
Message message = conversation.findUnsentMessageWithUuid(uuid);
if (message != null) {
markMessage(message, Message.STATUS_SEND);
if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
databaseBackend.updateConversation(conversation);
}
}
}
}
}
};
private final IqGenerator mIqGenerator = new IqGenerator(this);
public DatabaseBackend databaseBackend;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@Override
public void onContactStatusChanged(Contact contact, boolean online) {
Conversation conversation = find(getConversations(), contact);
if (conversation != null) {
if (online) {
conversation.endOtrIfNeeded();
if (contact.getPresences().size() == 1) {
sendUnsentMessages(conversation);
}
} else {
if (contact.getPresences().size() >= 1) {
if (conversation.hasValidOtrSession()) {
String otrResource = conversation.getOtrSession().getSessionID().getUserID();
if (!(Arrays.asList(contact.getPresences().asStringArray()).contains(otrResource))) {
conversation.endOtrIfNeeded();
}
}
} else {
conversation.endOtrIfNeeded();
}
}
}
}
};
private FileBackend fileBackend = new FileBackend(this); private FileBackend fileBackend = new FileBackend(this);
private MemorizingTrustManager mMemorizingTrustManager; private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService( private NotificationService mNotificationService = new NotificationService(
@ -244,18 +162,103 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private List<Account> accounts; private List<Account> accounts;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this); this);
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@Override
public void onContactStatusChanged(Contact contact, boolean online) {
Conversation conversation = find(getConversations(), contact);
if (conversation != null) {
if (online) {
conversation.endOtrIfNeeded();
if (contact.getPresences().size() == 1) {
sendUnsentMessages(conversation);
}
} else {
if (contact.getPresences().size() >= 1) {
if (conversation.hasValidOtrSession()) {
String otrResource = conversation.getOtrSession().getSessionID().getUserID();
if (!(Arrays.asList(contact.getPresences().asStringArray()).contains(otrResource))) {
conversation.endOtrIfNeeded();
}
}
} else {
conversation.endOtrIfNeeded();
}
}
}
}
};
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this); this);
private AvatarService mAvatarService = new AvatarService(this); private AvatarService mAvatarService = new AvatarService(this);
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null; private OnConversationUpdate mOnConversationUpdate = null;
private final FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsImageDirectory()) {
@Override
public void onEvent(int event, String path) {
if (event == FileObserver.DELETE) {
markFileDeleted(path.split("\\.")[0]);
}
}
};
private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
public void onJinglePacketReceived(Account account, JinglePacket packet) {
mJingleConnectionManager.deliverPacket(account, packet);
}
};
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
@Override
public void onMessageAcknowledged(Account account, String uuid) {
for (final Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
Message message = conversation.findUnsentMessageWithUuid(uuid);
if (message != null) {
markMessage(message, Message.STATUS_SEND);
if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
databaseBackend.updateConversation(conversation);
}
}
}
}
}
};
private int convChangedListenerCount = 0; private int convChangedListenerCount = 0;
private OnShowErrorToast mOnShowErrorToast = null; private OnShowErrorToast mOnShowErrorToast = null;
private int showErrorToastListenerCount = 0; private int showErrorToastListenerCount = 0;
private int unreadCount = -1; private int unreadCount = -1;
private OnAccountUpdate mOnAccountUpdate = null; private OnAccountUpdate mOnAccountUpdate = null;
private OnCaptchaRequested mOnCaptchaRequested = null; private OnCaptchaRequested mOnCaptchaRequested = null;
private int accountChangedListenerCount = 0;
private int captchaRequestedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
private OnUpdateBlocklist mOnUpdateBlocklist = null;
private int updateBlocklistListenerCount = 0;
private int rosterChangedListenerCount = 0;
private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0;
private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
private int keyStatusUpdatedListenerCount = 0;
private SecureRandom mRandom;
private final OnBindListener mOnBindListener = new OnBindListener() {
@Override
public void onBind(final Account account) {
account.getRoster().clearPresences();
fetchRosterFromServer(account);
fetchBookmarks(account);
sendPresence(account);
connectMultiModeConversations(account);
mMessageArchiveService.executePendingQueries(account);
mJingleConnectionManager.cancelInTransmission();
syncDirtyContacts(account);
account.getAxolotlService().publishBundlesIfNeeded(true, false);
}
};
private OnStatusChanged statusListener = new OnStatusChanged() { private OnStatusChanged statusListener = new OnStatusChanged() {
@Override @Override
@ -313,17 +316,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
getNotificationService().updateErrorNotification(); getNotificationService().updateErrorNotification();
} }
}; };
private int accountChangedListenerCount = 0;
private int captchaRequestedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
private OnUpdateBlocklist mOnUpdateBlocklist = null;
private int updateBlocklistListenerCount = 0;
private int rosterChangedListenerCount = 0;
private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0;
private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
private int keyStatusUpdatedListenerCount = 0;
private SecureRandom mRandom;
private OpenPgpServiceConnection pgpServiceConnection; private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null; private PgpEngine mPgpEngine = null;
private WakeLock wakeLock; private WakeLock wakeLock;
@ -333,6 +325,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private EventReceiver mEventReceiver = new EventReceiver(); private EventReceiver mEventReceiver = new EventReceiver();
private boolean mRestoredFromDatabase = false; private boolean mRestoredFromDatabase = false;
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
}
public boolean areMessagesInitialized() { public boolean areMessagesInitialized() {
return this.mRestoredFromDatabase; return this.mRestoredFromDatabase;
} }
@ -1338,6 +1335,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
callback.informUser(R.string.account_already_exists); callback.informUser(R.string.account_already_exists);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
callback.informUser(R.string.unable_to_parse_certificate); callback.informUser(R.string.unable_to_parse_certificate);
} }
} }
@ -1364,13 +1362,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else { } else {
showErrorToastInUi(R.string.jid_does_not_match_certificate); showErrorToastInUi(R.string.jid_does_not_match_certificate);
} }
} catch (InterruptedException e) { } catch (Exception e) {
e.printStackTrace();
} catch (KeyChainException e) {
e.printStackTrace();
} catch (InvalidJidException e) {
e.printStackTrace();
} catch (CertificateEncodingException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -1378,7 +1370,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void updateAccount(final Account account) { public void updateAccount(final Account account) {
this.statusListener.onStatusChanged(account); this.statusListener.onStatusChanged(account);
databaseBackend.updateAccount(account); databaseBackend.updateAccount(account);
reconnectAccount(account, false, true); reconnectAccountInBackground(account);
updateAccountUi(); updateAccountUi();
getNotificationService().updateErrorNotification(); getNotificationService().updateErrorNotification();
} }
@ -1791,10 +1783,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
public void leaveMuc(Conversation conversation) { public void leaveMuc(Conversation conversation) {
leaveMuc(conversation, false);
}
private void leaveMuc(Conversation conversation, boolean now) {
Account account = conversation.getAccount(); Account account = conversation.getAccount();
account.pendingConferenceJoins.remove(conversation); account.pendingConferenceJoins.remove(conversation);
account.pendingConferenceLeaves.remove(conversation); account.pendingConferenceLeaves.remove(conversation);
if (account.getStatus() == Account.State.ONLINE) { if (account.getStatus() == Account.State.ONLINE || now) {
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setTo(conversation.getJid()); packet.setTo(conversation.getJid());
packet.setFrom(conversation.getAccount().getJid()); packet.setFrom(conversation.getAccount().getJid());
@ -2006,7 +2002,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}); });
} }
public void disconnect(Account account, boolean force) { private void disconnect(Account account, boolean force) {
if ((account.getStatus() == Account.State.ONLINE) if ((account.getStatus() == Account.State.ONLINE)
|| (account.getStatus() == Account.State.DISABLED)) { || (account.getStatus() == Account.State.DISABLED)) {
if (!force) { if (!force) {
@ -2014,7 +2010,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
for (Conversation conversation : conversations) { for (Conversation conversation : conversations) {
if (conversation.getAccount() == account) { if (conversation.getAccount() == account) {
if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getMode() == Conversation.MODE_MULTI) {
leaveMuc(conversation); leaveMuc(conversation, true);
} else { } else {
if (conversation.endOtrIfNeeded()) { if (conversation.endOtrIfNeeded()) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
@ -2204,10 +2200,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
fetchAvatar(account, avatar, null); fetchAvatar(account, avatar, null);
} }
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum;
}
public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
final String KEY = generateFetchKey(account, avatar); final String KEY = generateFetchKey(account, avatar);
synchronized (this.mInProgressAvatarFetches) { synchronized (this.mInProgressAvatarFetches) {
@ -2385,6 +2377,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (account.getXmppConnection() == null) { if (account.getXmppConnection() == null) {
account.setXmppConnection(createConnection(account)); account.setXmppConnection(createConnection(account));
} else if (!force) {
try {
Log.d(Config.LOGTAG, "wait for disconnect");
Thread.sleep(500); //sleep wait for disconnect
} catch (InterruptedException e) {
//ignored
}
} }
Thread thread = new Thread(account.getXmppConnection()); Thread thread = new Thread(account.getXmppConnection());
account.getXmppConnection().setInteractive(interactive); account.getXmppConnection().setInteractive(interactive);
@ -2840,6 +2839,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public interface OnAccountCreated { public interface OnAccountCreated {
void onAccountCreated(Account account); void onAccountCreated(Account account);
void informUser(int r); void informUser(int r);
} }

View file

@ -1139,16 +1139,16 @@ public class XmppConnection implements Runnable {
} }
public void disconnect(final boolean force) { public void disconnect(final boolean force) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
try {
if (force) { if (force) {
try {
socket.close(); socket.close();
return; } catch(Exception e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")");
} }
new Thread(new Runnable() { return;
} else {
@Override resetStreamId();
public void run() {
if (tagWriter.isActive()) { if (tagWriter.isActive()) {
tagWriter.finish(); tagWriter.finish();
try { try {
@ -1165,19 +1165,16 @@ public class XmppConnection implements Runnable {
if (warned) { if (warned) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": tag writer has finished");
} }
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closing stream");
tagWriter.writeTag(Tag.end("stream:stream")); tagWriter.writeTag(Tag.end("stream:stream"));
socket.close(); socket.close();
} catch (final IOException e) { } catch (final IOException e) {
Log.d(Config.LOGTAG,"io exception during disconnect"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": io exception during disconnect ("+e.getMessage()+")");
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
Log.d(Config.LOGTAG, "interrupted"); Log.d(Config.LOGTAG, "interrupted");
} }
} }
} }
}).start();
} catch (final IOException e) {
Log.d(Config.LOGTAG, "io exception during disconnect");
}
} }
public void resetStreamId() { public void resetStreamId() {