presist http upload availibility

This commit is contained in:
Daniel Gultsch 2018-03-18 10:30:15 +01:00
parent f2ea609b51
commit 5514958e93
4 changed files with 209 additions and 277 deletions

View file

@ -54,6 +54,7 @@ public class Account extends AbstractEntity {
public static final int OPTION_MAGIC_CREATE = 4; public static final int OPTION_MAGIC_CREATE = 4;
public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5; public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6; public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>(); public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
public boolean httpUploadAvailable(long filesize) { public boolean httpUploadAvailable(long filesize) {
@ -61,7 +62,7 @@ public class Account extends AbstractEntity {
} }
public boolean httpUploadAvailable() { public boolean httpUploadAvailable() {
return httpUploadAvailable(0); return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
} }
public void setDisplayName(String displayName) { public void setDisplayName(String displayName) {

View file

@ -1,5 +1,7 @@
package eu.siacs.conversations.http; package eu.siacs.conversations.http;
import android.util.Log;
import org.apache.http.conn.ssl.StrictHostnameVerifier; import org.apache.http.conn.ssl.StrictHostnameVerifier;
import java.io.IOException; import java.io.IOException;
@ -16,10 +18,13 @@ import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.TLSSocketFactory; import eu.siacs.conversations.utils.TLSSocketFactory;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
public class HttpConnectionManager extends AbstractConnectionManager { public class HttpConnectionManager extends AbstractConnectionManager {

View file

@ -136,10 +136,6 @@ import rocks.xmpp.addr.Jid;
public class XmppConnectionService extends Service { public class XmppConnectionService extends Service {
static {
URL.setURLStreamHandlerFactory(new AesGcmURLStreamHandlerFactory());
}
public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations"; public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations";
public static final String ACTION_MARK_AS_READ = "mark_as_read"; public static final String ACTION_MARK_AS_READ = "mark_as_read";
public static final String ACTION_SNOOZE = "snooze"; public static final String ACTION_SNOOZE = "snooze";
@ -147,24 +143,28 @@ public class XmppConnectionService extends Service {
public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error"; public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
public static final String ACTION_TRY_AGAIN = "try_again"; public static final String ACTION_TRY_AGAIN = "try_again";
public static final String ACTION_IDLE_PING = "idle_ping"; public static final String ACTION_IDLE_PING = "idle_ping";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh"; public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received"; public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
static {
URL.setURLStreamHandlerFactory(new AesGcmURLStreamHandlerFactory());
}
public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor("FileAdding"); private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor("FileAdding");
private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression"); private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression");
private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter"); private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader"); private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor"); private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor");
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
private final IBinder mBinder = new XmppConnectionBinder(); private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private final IqGenerator mIqGenerator = new IqGenerator(this); private final IqGenerator mIqGenerator = new IqGenerator(this);
private final List<String> mInProgressAvatarFetches = new ArrayList<>(); private final List<String> mInProgressAvatarFetches = new ArrayList<>();
private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>(); private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
private long mLastActivity = 0;
public DatabaseBackend databaseBackend; public DatabaseBackend databaseBackend;
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
private long mLastActivity = 0;
private ContentObserver contactObserver = new ContentObserver(null) { private ContentObserver contactObserver = new ContentObserver(null) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
@ -197,10 +197,6 @@ public class XmppConnectionService extends Service {
} }
}; };
private MessageGenerator mMessageGenerator = new MessageGenerator(this); private MessageGenerator mMessageGenerator = new MessageGenerator(this);
private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
private List<Account> accounts;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@Override @Override
@ -215,14 +211,23 @@ public class XmppConnectionService extends Service {
} }
} }
}; };
private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
private List<Account> accounts;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
public void onJinglePacketReceived(Account account, JinglePacket packet) {
mJingleConnectionManager.deliverPacket(account, packet);
}
};
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 MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private PushManagementService mPushManagementService = new PushManagementService(this); private PushManagementService mPushManagementService = new PushManagementService(this);
private OnConversationUpdate mOnConversationUpdate = null; private OnConversationUpdate mOnConversationUpdate = null;
private final ConversationsFileObserver fileObserver = new ConversationsFileObserver( private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
Environment.getExternalStorageDirectory().getAbsolutePath() Environment.getExternalStorageDirectory().getAbsolutePath()
) { ) {
@ -231,13 +236,6 @@ public class XmppConnectionService extends Service {
markFileDeleted(path); markFileDeleted(path);
} }
}; };
private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() {
@Override
public void onJinglePacketReceived(Account account, JinglePacket packet) {
mJingleConnectionManager.deliverPacket(account, packet);
}
};
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
@Override @Override
@ -267,10 +265,6 @@ public class XmppConnectionService extends Service {
private OnMucRosterUpdate mOnMucRosterUpdate = null; private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0; private int mucRosterChangedListenerCount = 0;
private OnKeyStatusUpdated mOnKeyStatusUpdated = null; private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
private int keyStatusUpdatedListenerCount = 0;
private AtomicLong mLastExpiryRun = new AtomicLong(0);
private SecureRandom mRandom;
private LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
private final OnBindListener mOnBindListener = new OnBindListener() { private final OnBindListener mOnBindListener = new OnBindListener() {
@Override @Override
@ -283,19 +277,22 @@ public class XmppConnectionService extends Service {
} }
} }
} }
if (account.setOption(Account.OPTION_LOGGED_IN_SUCCESSFULLY, true)) { boolean needsUpdating = account.setOption(Account.OPTION_LOGGED_IN_SUCCESSFULLY, true);
needsUpdating |= account.setOption(Account.OPTION_HTTP_UPLOAD_AVAILABLE, account.getXmppConnection().getFeatures().httpUpload(0));
if (needsUpdating) {
Log.d(Config.LOGTAG, "account needed updating");
databaseBackend.updateAccount(account); databaseBackend.updateAccount(account);
} }
account.getRoster().clearPresences(); account.getRoster().clearPresences();
mJingleConnectionManager.cancelInTransmission(); mJingleConnectionManager.cancelInTransmission();
fetchRosterFromServer(account); fetchRosterFromServer(account);
fetchBookmarks(account); fetchBookmarks(account);
final boolean flexible= account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval(); final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval();
final boolean catchup = getMessageArchiveService().inCatchup(account); final boolean catchup = getMessageArchiveService().inCatchup(account);
if (flexible && catchup) { if (flexible && catchup) {
sendIqPacket(account, mIqGenerator.purgeOfflineMessages(), (acc, packet) -> { sendIqPacket(account, mIqGenerator.purgeOfflineMessages(), (acc, packet) -> {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IqPacket.TYPE.RESULT) {
Log.d(Config.LOGTAG, acc.getJid().asBareJid()+": successfully purged offline messages"); Log.d(Config.LOGTAG, acc.getJid().asBareJid() + ": successfully purged offline messages");
} }
}); });
} }
@ -307,6 +304,10 @@ public class XmppConnectionService extends Service {
syncDirtyContacts(account); syncDirtyContacts(account);
} }
}; };
private int keyStatusUpdatedListenerCount = 0;
private AtomicLong mLastExpiryRun = new AtomicLong(0);
private SecureRandom mRandom;
private LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
private OnStatusChanged statusListener = new OnStatusChanged() { private OnStatusChanged statusListener = new OnStatusChanged() {
@Override @Override
@ -379,6 +380,16 @@ public class XmppConnectionService extends Service {
getNotificationService().updateErrorNotification(); getNotificationService().updateErrorNotification();
} }
}; };
private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null;
private WakeLock wakeLock;
private PowerManager pm;
private LruCache<String, Bitmap> mBitmapCache;
private EventReceiver mEventReceiver = new EventReceiver();
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().asBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
}
private boolean isInLowPingTimeoutMode(Account account) { private boolean isInLowPingTimeoutMode(Account account) {
synchronized (mLowPingTimeoutMode) { synchronized (mLowPingTimeoutMode) {
@ -396,19 +407,6 @@ public class XmppConnectionService extends Service {
toggleForegroundService(); toggleForegroundService();
} }
private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null;
private WakeLock wakeLock;
private PowerManager pm;
private LruCache<String, Bitmap> mBitmapCache;
private EventReceiver mEventReceiver = new EventReceiver();
public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().asBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
}
public boolean areMessagesInitialized() { public boolean areMessagesInitialized() {
return this.restoredFromDatabaseLatch.getCount() == 0; return this.restoredFromDatabaseLatch.getCount() == 0;
} }
@ -447,7 +445,7 @@ public class XmppConnectionService extends Service {
return this.mAvatarService; return this.mAvatarService;
} }
public void attachLocationToConversation(final Conversation conversation,final Uri uri, final UiCallback<Message> callback) { public void attachLocationToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
int encryption = conversation.getNextEncryption(); int encryption = conversation.getNextEncryption();
if (encryption == Message.ENCRYPTION_PGP) { if (encryption == Message.ENCRYPTION_PGP) {
encryption = Message.ENCRYPTION_DECRYPTED; encryption = Message.ENCRYPTION_DECRYPTED;
@ -511,26 +509,22 @@ public class XmppConnectionService extends Service {
} }
message.setCounterpart(conversation.getNextCounterpart()); message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_IMAGE); message.setType(Message.TYPE_IMAGE);
mFileAddingExecutor.execute(new Runnable() { mFileAddingExecutor.execute(() -> {
try {
@Override getFileBackend().copyImageToPrivateStorage(message, uri);
public void run() { if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
try { final PgpEngine pgpEngine = getPgpEngine();
getFileBackend().copyImageToPrivateStorage(message, uri); if (pgpEngine != null) {
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { pgpEngine.encrypt(message, callback);
final PgpEngine pgpEngine = getPgpEngine(); } else if (callback != null) {
if (pgpEngine != null) { callback.error(R.string.unable_to_connect_to_keychain, null);
pgpEngine.encrypt(message, callback);
} else if (callback != null) {
callback.error(R.string.unable_to_connect_to_keychain, null);
}
} else {
sendMessage(message);
callback.success(message);
} }
} catch (final FileBackend.FileCopyException e) { } else {
callback.error(e.getResId(), message); sendMessage(message);
callback.success(message);
} }
} catch (final FileBackend.FileCopyException e) {
callback.error(e.getResId(), message);
} }
}); });
} }
@ -576,7 +570,7 @@ public class XmppConnectionService extends Service {
restoredFromDatabaseLatch.await(); restoredFromDatabaseLatch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process clear notification"); Log.d(Config.LOGTAG, "unable to process clear notification");
} }
}); });
break; break;
@ -597,7 +591,7 @@ public class XmppConnectionService extends Service {
if (body == null || body.length() <= 0) { if (body == null || body.length() <= 0) {
break; break;
} }
mNotificationExecutor.execute(()-> { mNotificationExecutor.execute(() -> {
try { try {
restoredFromDatabaseLatch.await(); restoredFromDatabaseLatch.await();
final Conversation c = findConversationByUuid(uuid); final Conversation c = findConversationByUuid(uuid);
@ -605,7 +599,7 @@ public class XmppConnectionService extends Service {
directReply(c, body.toString(), dismissNotification); directReply(c, body.toString(), dismissNotification);
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process direct reply"); Log.d(Config.LOGTAG, "unable to process direct reply");
} }
}); });
break; break;
@ -620,7 +614,7 @@ public class XmppConnectionService extends Service {
restoredFromDatabaseLatch.await(); restoredFromDatabaseLatch.await();
sendReadMarker(c); sendReadMarker(c);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process notification read marker for conversation "+c.getName()); Log.d(Config.LOGTAG, "unable to process notification read marker for conversation " + c.getName());
} }
}); });
@ -629,7 +623,7 @@ public class XmppConnectionService extends Service {
mNotificationExecutor.execute(() -> { mNotificationExecutor.execute(() -> {
final Conversation c = findConversationByUuid(uuid); final Conversation c = findConversationByUuid(uuid);
if (c == null) { if (c == null) {
Log.d(Config.LOGTAG,"received snooze intent for unknown conversation ("+ uuid +")"); Log.d(Config.LOGTAG, "received snooze intent for unknown conversation (" + uuid + ")");
return; return;
} }
c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000); c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000);
@ -663,7 +657,7 @@ public class XmppConnectionService extends Service {
case Intent.ACTION_SEND: case Intent.ACTION_SEND:
Uri uri = intent.getData(); Uri uri = intent.getData();
if (uri != null) { if (uri != null) {
Log.d(Config.LOGTAG, "received uri permission for "+uri.toString()); Log.d(Config.LOGTAG, "received uri permission for " + uri.toString());
} }
return START_STICKY; return START_STICKY;
} }
@ -912,23 +906,20 @@ public class XmppConnectionService extends Service {
public void expireOldMessages(final boolean resetHasMessagesLeftOnServer) { public void expireOldMessages(final boolean resetHasMessagesLeftOnServer) {
mLastExpiryRun.set(SystemClock.elapsedRealtime()); mLastExpiryRun.set(SystemClock.elapsedRealtime());
mDatabaseWriterExecutor.execute(new Runnable() { mDatabaseWriterExecutor.execute(() -> {
@Override long timestamp = getAutomaticMessageDeletionDate();
public void run() { if (timestamp > 0) {
long timestamp = getAutomaticMessageDeletionDate(); databaseBackend.expireOldMessages(timestamp);
if (timestamp > 0) { synchronized (XmppConnectionService.this.conversations) {
databaseBackend.expireOldMessages(timestamp); for (Conversation conversation : XmppConnectionService.this.conversations) {
synchronized (XmppConnectionService.this.conversations) { conversation.expireOldMessages(timestamp);
for (Conversation conversation : XmppConnectionService.this.conversations) { if (resetHasMessagesLeftOnServer) {
conversation.expireOldMessages(timestamp); conversation.messagesLoaded.set(true);
if (resetHasMessagesLeftOnServer) { conversation.setHasMessagesLeftOnServer(true);
conversation.messagesLoaded.set(true);
conversation.setHasMessagesLeftOnServer(true);
}
} }
} }
updateConversationUi();
} }
updateConversationUi();
} }
}); });
} }
@ -939,7 +930,7 @@ public class XmppConnectionService extends Service {
final NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); final NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnected(); return activeNetwork != null && activeNetwork.isConnected();
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.d(Config.LOGTAG,"unable to check for internet connection",e); Log.d(Config.LOGTAG, "unable to check for internet connection", e);
return true; //if internet connection can not be checked it is probably best to just try return true; //if internet connection can not be checked it is probably best to just try
} }
} }
@ -976,12 +967,7 @@ public class XmppConnectionService extends Service {
restoreFromDatabase(); restoreFromDatabase();
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver); getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
new Thread(new Runnable() { new Thread(() -> fileObserver.startWatching()).start();
@Override
public void run() {
fileObserver.startWatching();
}
}).start();
if (Config.supportOpenPgp()) { if (Config.supportOpenPgp()) {
this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() { this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
@Override @Override
@ -1081,12 +1067,7 @@ public class XmppConnectionService extends Service {
} }
databaseBackend.writeRoster(account.getRoster()); databaseBackend.writeRoster(account.getRoster());
if (account.getXmppConnection() != null) { if (account.getXmppConnection() != null) {
new Thread(new Runnable() { new Thread(() -> disconnect(account, false)).start();
@Override
public void run() {
disconnect(account, false);
}
}).start();
} }
} }
if (stop || activeAccounts == 0) { if (stop || activeAccounts == 0) {
@ -1399,97 +1380,89 @@ public class XmppConnectionService extends Service {
} }
long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore; long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore;
Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms"); Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms");
Runnable runnable = new Runnable() { Runnable runnable = () -> {
@Override long deletionDate = getAutomaticMessageDeletionDate();
public void run() { mLastExpiryRun.set(SystemClock.elapsedRealtime());
long deletionDate = getAutomaticMessageDeletionDate(); if (deletionDate > 0) {
mLastExpiryRun.set(SystemClock.elapsedRealtime()); Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate));
if (deletionDate > 0) { databaseBackend.expireOldMessages(deletionDate);
Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate));
databaseBackend.expireOldMessages(deletionDate);
}
Log.d(Config.LOGTAG, "restoring roster...");
for (Account account : accounts) {
databaseBackend.readRoster(account.getRoster());
account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage
}
getBitmapCache().evictAll();
loadPhoneContacts();
Log.d(Config.LOGTAG, "restoring messages...");
final long startMessageRestore = SystemClock.elapsedRealtime();
for (Conversation conversation : conversations) {
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
checkDeletedFiles(conversation);
conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
markMessage(message, Message.STATUS_WAITING);
}
});
conversation.findUnreadMessages(new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
mNotificationService.pushFromBacklog(message);
}
});
}
mNotificationService.finishBacklog(false);
restoredFromDatabaseLatch.countDown();
final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore;
Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms");
updateConversationUi();
} }
Log.d(Config.LOGTAG, "restoring roster...");
for (Account account : accounts) {
databaseBackend.readRoster(account.getRoster());
account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage
}
getBitmapCache().evictAll();
loadPhoneContacts();
Log.d(Config.LOGTAG, "restoring messages...");
final long startMessageRestore = SystemClock.elapsedRealtime();
for (Conversation conversation : conversations) {
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
checkDeletedFiles(conversation);
conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
markMessage(message, Message.STATUS_WAITING);
}
});
conversation.findUnreadMessages(new Conversation.OnMessageFound() {
@Override
public void onMessageFound(Message message) {
mNotificationService.pushFromBacklog(message);
}
});
}
mNotificationService.finishBacklog(false);
restoredFromDatabaseLatch.countDown();
final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore;
Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms");
updateConversationUi();
}; };
mDatabaseReaderExecutor.execute(runnable); //will contain one write command (expiry) but that's fine mDatabaseReaderExecutor.execute(runnable); //will contain one write command (expiry) but that's fine
} }
} }
public void loadPhoneContacts() { public void loadPhoneContacts() {
mContactMergerExecutor.execute(new Runnable() { mContactMergerExecutor.execute(() -> PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() {
@Override @Override
public void run() { public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
PhoneHelper.loadPhoneContacts(XmppConnectionService.this, new OnPhoneContactsLoadedListener() { Log.d(Config.LOGTAG, "start merging phone contacts with roster");
@Override for (Account account : accounts) {
public void onPhoneContactsLoaded(List<Bundle> phoneContacts) { List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts();
Log.d(Config.LOGTAG, "start merging phone contacts with roster"); for (Bundle phoneContact : phoneContacts) {
for (Account account : accounts) { Jid jid;
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(); try {
for (Bundle phoneContact : phoneContacts) { jid = Jid.of(phoneContact.getString("jid"));
Jid jid; } catch (final IllegalArgumentException e) {
try { continue;
jid = Jid.of(phoneContact.getString("jid"));
} catch (final IllegalArgumentException e) {
continue;
}
final Contact contact = account.getRoster().getContact(jid);
String systemAccount = phoneContact.getInt("phoneid")
+ "#"
+ phoneContact.getString("lookup");
contact.setSystemAccount(systemAccount);
boolean needsCacheClean = contact.setPhotoUri(phoneContact.getString("photouri"));
needsCacheClean |= contact.setSystemName(phoneContact.getString("displayname"));
if (needsCacheClean) {
getAvatarService().clear(contact);
}
withSystemAccounts.remove(contact);
}
for (Contact contact : withSystemAccounts) {
contact.setSystemAccount(null);
boolean needsCacheClean = contact.setPhotoUri(null);
needsCacheClean |= contact.setSystemName(null);
if (needsCacheClean) {
getAvatarService().clear(contact);
}
}
} }
Log.d(Config.LOGTAG, "finished merging phone contacts"); final Contact contact = account.getRoster().getContact(jid);
mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false, true)); String systemAccount = phoneContact.getInt("phoneid")
updateAccountUi(); + "#"
+ phoneContact.getString("lookup");
contact.setSystemAccount(systemAccount);
boolean needsCacheClean = contact.setPhotoUri(phoneContact.getString("photouri"));
needsCacheClean |= contact.setSystemName(phoneContact.getString("displayname"));
if (needsCacheClean) {
getAvatarService().clear(contact);
}
withSystemAccounts.remove(contact);
} }
}); for (Contact contact : withSystemAccounts) {
contact.setSystemAccount(null);
boolean needsCacheClean = contact.setPhotoUri(null);
needsCacheClean |= contact.setSystemName(null);
if (needsCacheClean) {
getAvatarService().clear(contact);
}
}
}
Log.d(Config.LOGTAG, "finished merging phone contacts");
mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false, true));
updateAccountUi();
} }
}); }));
} }
public List<Conversation> getConversations() { public List<Conversation> getConversations() {
@ -1497,16 +1470,12 @@ public class XmppConnectionService extends Service {
} }
private void checkDeletedFiles(Conversation conversation) { private void checkDeletedFiles(Conversation conversation) {
conversation.findMessagesWithFiles(new Conversation.OnMessageFound() { conversation.findMessagesWithFiles(message -> {
if (!getFileBackend().isFileAvailable(message)) {
@Override message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
public void onMessageFound(Message message) { final int s = message.getStatus();
if (!getFileBackend().isFileAvailable(message)) { if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); markMessage(message, Message.STATUS_SEND_FAILED);
final int s = message.getStatus();
if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
markMessage(message, Message.STATUS_SEND_FAILED);
}
} }
} }
}); });
@ -1515,22 +1484,19 @@ public class XmppConnectionService extends Service {
private void markFileDeleted(final String path) { private void markFileDeleted(final String path) {
Log.d(Config.LOGTAG, "deleted file " + path); Log.d(Config.LOGTAG, "deleted file " + path);
for (Conversation conversation : getConversations()) { for (Conversation conversation : getConversations()) {
conversation.findMessagesWithFiles(new Conversation.OnMessageFound() { conversation.findMessagesWithFiles(message -> {
@Override DownloadableFile file = fileBackend.getFile(message);
public void onMessageFound(Message message) { if (file.getAbsolutePath().equals(path)) {
DownloadableFile file = fileBackend.getFile(message); if (!file.exists()) {
if (file.getAbsolutePath().equals(path)) { message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
if (!file.exists()) { final int s = message.getStatus();
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
final int s = message.getStatus(); markMessage(message, Message.STATUS_SEND_FAILED);
if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
markMessage(message, Message.STATUS_SEND_FAILED);
} else {
updateConversationUi();
}
} else { } else {
Log.d(Config.LOGTAG, "found matching message for file " + path + " but file still exists"); updateConversationUi();
} }
} else {
Log.d(Config.LOGTAG, "found matching message for file " + path + " but file still exists");
} }
} }
}); });
@ -2348,7 +2314,7 @@ public class XmppConnectionService extends Service {
final Conversation conversation = self.getConversation(); final Conversation conversation = self.getConversation();
Jid full = self.getFullJid(); Jid full = self.getFullJid();
if (!full.equals(conversation.getJid())) { if (!full.equals(conversation.getJid())) {
Log.d(Config.LOGTAG,"nick changed. updating"); Log.d(Config.LOGTAG, "nick changed. updating");
conversation.setContactJid(full); conversation.setContactJid(full);
databaseBackend.updateConversation(conversation); databaseBackend.updateConversation(conversation);
} }
@ -3066,12 +3032,7 @@ public class XmppConnectionService extends Service {
} }
public void reconnectAccountInBackground(final Account account) { public void reconnectAccountInBackground(final Account account) {
new Thread(new Runnable() { new Thread(() -> reconnectAccount(account, false, true)).start();
@Override
public void run() {
reconnectAccount(account, false, true);
}
}).start();
} }
public void invite(Conversation conversation, Jid contact) { public void invite(Conversation conversation, Jid contact) {
@ -3298,12 +3259,9 @@ public class XmppConnectionService extends Service {
} }
final List<Message> readMessages = conversation.markRead(); final List<Message> readMessages = conversation.markRead();
if (readMessages.size() > 0) { if (readMessages.size() > 0) {
Runnable runnable = new Runnable() { Runnable runnable = () -> {
@Override for (Message message : readMessages) {
public void run() { databaseBackend.updateMessage(message);
for (Message message : readMessages) {
databaseBackend.updateMessage(message);
}
} }
}; };
mDatabaseWriterExecutor.execute(runnable); mDatabaseWriterExecutor.execute(runnable);
@ -3369,26 +3327,10 @@ public class XmppConnectionService extends Service {
setMemorizingTrustManager(tm); setMemorizingTrustManager(tm);
} }
public PowerManager getPowerManager() {
return this.pm;
}
public LruCache<String, Bitmap> getBitmapCache() { public LruCache<String, Bitmap> getBitmapCache() {
return this.mBitmapCache; return this.mBitmapCache;
} }
public void syncRosterToDisk(final Account account) {
Runnable runnable = new Runnable() {
@Override
public void run() {
databaseBackend.writeRoster(account.getRoster());
}
};
mDatabaseWriterExecutor.execute(runnable);
}
public Collection<String> getKnownHosts() { public Collection<String> getKnownHosts() {
final Set<String> hosts = new HashSet<>(); final Set<String> hosts = new HashSet<>();
for (final Account account : getAccounts()) { for (final Account account : getAccounts()) {
@ -3449,7 +3391,7 @@ public class XmppConnectionService extends Service {
final XmppConnection connection = account.getXmppConnection(); final XmppConnection connection = account.getXmppConnection();
if (connection != null) { if (connection != null) {
IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data); IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data);
connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener,true); connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener, true);
} }
} }
@ -3471,7 +3413,7 @@ public class XmppConnectionService extends Service {
} else { } else {
status = getTargetPresence(); status = getTargetPresence();
} }
PresencePacket packet = mPresenceGenerator.selfPresence(account,status); PresencePacket packet = mPresenceGenerator.selfPresence(account, status);
String message = account.getPresenceStatusMessage(); String message = account.getPresenceStatusMessage();
if (message != null && !message.isEmpty()) { if (message != null && !message.isEmpty()) {
packet.addChild(new Element("status").setContent(message)); packet.addChild(new Element("status").setContent(message));
@ -3600,12 +3542,9 @@ public class XmppConnectionService extends Service {
conversation.clearMessages(); conversation.clearMessages();
conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
conversation.setLastClearHistory(clearDate, reference); conversation.setLastClearHistory(clearDate, reference);
Runnable runnable = new Runnable() { Runnable runnable = () -> {
@Override databaseBackend.deleteMessagesInConversation(conversation);
public void run() { databaseBackend.updateConversation(conversation);
databaseBackend.deleteMessagesInConversation(conversation);
databaseBackend.updateConversation(conversation);
}
}; };
mDatabaseWriterExecutor.execute(runnable); mDatabaseWriterExecutor.execute(runnable);
} }
@ -3675,12 +3614,9 @@ public class XmppConnectionService extends Service {
String displayName = account.getDisplayName(); String displayName = account.getDisplayName();
if (displayName != null && !displayName.isEmpty()) { if (displayName != null && !displayName.isEmpty()) {
IqPacket publish = mIqGenerator.publishNick(displayName); IqPacket publish = mIqGenerator.publishNick(displayName);
sendIqPacket(account, publish, new OnIqPacketReceived() { sendIqPacket(account, publish, (account1, packet) -> {
@Override if (packet.getType() == IqPacket.TYPE.ERROR) {
public void onIqPacketReceived(Account account, IqPacket packet) { Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": could not publish nick");
if (packet.getType() == IqPacket.TYPE.ERROR) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not publish nick");
}
} }
}); });
} }
@ -3711,20 +3647,17 @@ public class XmppConnectionService extends Service {
request.setTo(jid); request.setTo(jid);
request.query("http://jabber.org/protocol/disco#info"); request.query("http://jabber.org/protocol/disco#info");
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid);
sendIqPacket(account, request, new OnIqPacketReceived() { sendIqPacket(account, request, (account1, discoPacket) -> {
@Override if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
public void onIqPacketReceived(Account account, IqPacket discoPacket) { ServiceDiscoveryResult disco1 = new ServiceDiscoveryResult(discoPacket);
if (discoPacket.getType() == IqPacket.TYPE.RESULT) { if (presence.getVer().equals(disco1.getVer())) {
ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket); databaseBackend.insertDiscoveryResult(disco1);
if (presence.getVer().equals(disco.getVer())) { injectServiceDiscorveryResult(account1.getRoster(), presence.getHash(), presence.getVer(), disco1);
databaseBackend.insertDiscoveryResult(disco); } else {
injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco); Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco1.getVer());
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer());
}
} }
account.inProgressDiscoFetches.remove(key);
} }
account1.inProgressDiscoFetches.remove(key);
}); });
} }
} }
@ -3744,15 +3677,12 @@ public class XmppConnectionService extends Service {
final boolean legacy = account.getXmppConnection().getFeatures().mamLegacy(); final boolean legacy = account.getXmppConnection().getFeatures().mamLegacy();
IqPacket request = new IqPacket(IqPacket.TYPE.GET); IqPacket request = new IqPacket(IqPacket.TYPE.GET);
request.addChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM); request.addChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM);
sendIqPacket(account, request, new OnIqPacketReceived() { sendIqPacket(account, request, (account1, packet) -> {
@Override Element prefs = packet.findChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM);
public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) {
Element prefs = packet.findChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM); callback.onPreferencesFetched(prefs);
if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) { } else {
callback.onPreferencesFetched(prefs); callback.onPreferencesFetchFailed();
} else {
callback.onPreferencesFetchFailed();
}
} }
}); });
} }
@ -3859,18 +3789,18 @@ public class XmppConnectionService extends Service {
return mShortcutService; return mShortcutService;
} }
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
}
public void pushMamPreferences(Account account, Element prefs) { public void pushMamPreferences(Account account, Element prefs) {
IqPacket set = new IqPacket(IqPacket.TYPE.SET); IqPacket set = new IqPacket(IqPacket.TYPE.SET);
set.addChild(prefs); set.addChild(prefs);
sendIqPacket(account, set, null); sendIqPacket(account, set, null);
} }
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
}
public interface OnAccountCreated { public interface OnAccountCreated {
void onAccountCreated(Account account); void onAccountCreated(Account account);
@ -3910,10 +3840,7 @@ public class XmppConnectionService extends Service {
} }
public interface OnCaptchaRequested { public interface OnCaptchaRequested {
void onCaptchaRequested(Account account, void onCaptchaRequested(Account account, String id, Data data, Bitmap captcha);
String id,
Data data,
Bitmap captcha);
} }
public interface OnRosterUpdate { public interface OnRosterUpdate {

View file

@ -36,6 +36,7 @@ import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -135,7 +136,7 @@ public class XmppConnection implements Runnable {
private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>(); private final HashMap<Jid, ServiceDiscoveryResult> disco = new HashMap<>();
private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>(); private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>();
private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>(); private final Hashtable<String, Pair<IqPacket, OnIqPacketReceived>> packetCallbacks = new Hashtable<>();
private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>(); private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new HashSet<>();
private final XmppConnectionService mXmppConnectionService; private final XmppConnectionService mXmppConnectionService;
private Socket socket; private Socket socket;
private XmlReader tagReader; private XmlReader tagReader;
@ -1431,9 +1432,7 @@ public class XmppConnection implements Runnable {
} }
public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) { public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) { this.advancedStreamFeaturesLoadedListeners.add(listener);
this.advancedStreamFeaturesLoadedListeners.add(listener);
}
} }
private void forceCloseSocket() { private void forceCloseSocket() {