Support for sealed sender - Part 1
This commit is contained in:
parent
b7b9554364
commit
5f31762220
67 changed files with 1679 additions and 801 deletions
123
build.gradle
123
build.gradle
|
@ -77,7 +77,7 @@ dependencies {
|
|||
compile 'com.google.android.exoplayer:exoplayer-core:2.8.4'
|
||||
compile 'com.google.android.exoplayer:exoplayer-ui:2.8.4'
|
||||
|
||||
compile 'org.whispersystems:signal-service-android:2.9.0'
|
||||
compile 'org.whispersystems:signal-service-android:2.10.0-RC1'
|
||||
compile 'org.whispersystems:webrtc-android:M69'
|
||||
|
||||
compile "me.leolin:ShortcutBadger:1.1.16"
|
||||
|
@ -147,124 +147,6 @@ dependencies {
|
|||
}
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.android.support:design:7874ad1904eedc74aa41cffffb7f759d8990056f3bbbc9264911651c67c42f5f',
|
||||
'com.android.support:preference-v14:8133c6e19233fa51e036a341e6d3f4adeead3375cebf777efced0fe154c3267e',
|
||||
'com.android.support:preference-v7:75eabe936d1fc3b178450a554c4d433466036f2be6d6dccdf971eac9590fdbf5',
|
||||
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
|
||||
'pl.tajchert:waitingdots:2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c',
|
||||
'mobi.upod:time-duration-picker:db469ce0f48dd96b892eac424ed76870e54bf00fe0a28cdcddfbe5f2a226a0e1',
|
||||
'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794',
|
||||
'com.android.support:appcompat-v7:a3a8e5230359746ed91801579b5fbe4668e3b1c4e6a14c7d67c8f58cb0311752',
|
||||
'com.melnykov:floatingactionbutton:15d58d4fac0f7a288d0e5301bbaf501a146f5b3f5921277811bf99bd3b397263',
|
||||
'com.android.support:recyclerview-v7:eb296414c1f6d4c7b522f69fe50588ea85297855db0e7806c24eb4f75409587d',
|
||||
'com.android.support:support-v13:491f940c5d6d2ec7678fa2f14bd4bbbe8bf776e2c776d04bf0e5c2175975be43',
|
||||
'com.android.support:cardview-v7:bc9e6b0e06ce1205f1db34f0e6193019613d19cfeb54cdccea722340d1c60f26',
|
||||
'com.android.support:gridlayout-v7:5029529f7db66f8773426bf7318645f0840fc50d74f66355cd60c5e58d2da087',
|
||||
'com.android.support:exifinterface:bbf44e519edd6333a24a3285aa21fd00181b920b81ca8aa89a8899f03ab4d6b0',
|
||||
'android.arch.work:work-runtime:eda29b2cad202dee05a2e5aafe0a37c93ba9cde8f7cc0d0c8926a9f1a9498a8f',
|
||||
'android.arch.lifecycle:extensions:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6',
|
||||
'android.arch.lifecycle:common-java8:7078b5c8ccb94203df9cc2a463c69cf0021596e6cf966d78fbfd697aaafe0630',
|
||||
'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae',
|
||||
'com.google.android.gms:play-services-places:abf3a4a3b146ec7e6e753be62775e512868cf37d6f88ffe2d81167b33b57132b',
|
||||
'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e',
|
||||
'com.google.android.exoplayer:exoplayer-ui:027557b2d69b15e1852a2530b36971f0dcc177abae240ee35e05f63502cdb0a7',
|
||||
'com.google.android.exoplayer:exoplayer-core:e69b409e11887c955deb373357c30eeabf183395db0092b4817e0f80bb467d5b',
|
||||
'org.whispersystems:signal-service-android:bf469abcdcd2b2ba429024aca30713eaa3b83a77af34030a818b1c9fb4780044',
|
||||
'org.whispersystems:webrtc-android:5493c92141ce884fc5ce8240d783232f4fe14bd17a8d0d7d1bd4944d0bd1682f',
|
||||
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
|
||||
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
||||
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
|
||||
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
|
||||
'com.github.chrisbanes:PhotoView:ed06775308da260e1fd86d1d3288988fcd3d80db24ce0d7c9fcfedc39e622292',
|
||||
'com.github.bumptech.glide:glide:997de7ac95be6c944d3b8cbe13de11307736ea45451c1b09a6cec7c328ead59f',
|
||||
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
|
||||
'org.greenrobot:eventbus:180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c',
|
||||
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
|
||||
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',
|
||||
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
|
||||
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
|
||||
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
|
||||
'com.davemorrissey.labs:subsampling-scale-image-view:550c5baa07e0bb4ff0a18b705e96d34436d22619248bd8c08c08c730b1f55cfe',
|
||||
'cn.carbswang.android:NumberPickerView:18b3c316d62c7c277978a8d4ed57a5b8f4e943762264960f579a8a549c756729',
|
||||
'com.tomergoldst.android:tooltips:4c56697dd1ad64b8066535c61f961a6d901e7ae5d97ae27084ba40ad620349b6',
|
||||
'com.klinkerapps:android-smsmms:e7c3328a0f3a8dd44daa8129de4e99996f3057a4546e47891b036b81e0ebf1d1',
|
||||
'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9',
|
||||
'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1',
|
||||
'com.github.dmytrodanylyk.circular-progress-button:library:8dc6a29a5a8db7b2ad5a9a7fda1dc9ae0893f4c8f0545732b2c63854ea693e8e',
|
||||
'org.signal:android-database-sqlcipher:33d4063336893af00b9d68b418e7b290cace74c20ce8aacffddc0911010d3d73',
|
||||
'com.googlecode.ez-vcard:ez-vcard:7e24ad50b222d2f70ac91bdccfa3c0f6200b078d797cb784837f75e77bb4210f',
|
||||
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
|
||||
'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49',
|
||||
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086',
|
||||
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
|
||||
'com.android.support:support-v4:8b9031381c678d628c9e47b566ae1d161e1c9710f7855c759beeac7596cecf30',
|
||||
'com.android.support:support-fragment:3772fc738ada86824ba1a4b3f197c3dbd67b7ddcfe2c9db1de95ef2e3487a915',
|
||||
'com.android.support:animated-vector-drawable:271ecbc906cda8dcd9e655ba0473129c3408a4189c806f616c378e6fd18fb3b7',
|
||||
'com.android.support:support-core-ui:bbc7f65fc95649464733af373361532ab5f9f3b749c3badaa2bbf27e574b6c6f',
|
||||
'com.android.support:transition:45d09fc51284c17bbab300f5122512ac7d7348a6d23bda2051648bbe76cc9aa5',
|
||||
'com.android.support:viewpager:013c4c53058758ec104dbae970be58159f75dfe342ba8b937d15ff5282e35ffc',
|
||||
'com.android.support:coordinatorlayout:9dfacd80423dc979048fbaed83c0ee543c46259feb2417377e79a656888d3892',
|
||||
'com.android.support:drawerlayout:8f6809afae4793550c37461c9810e954ae6a23dbb4d23e5333bf18148df1150a',
|
||||
'com.android.support:slidingpanelayout:d1d234f66a1b36a9aee9b94fa6c66f97128c0828078c8e889e9037ec898cd600',
|
||||
'com.android.support:customview:98db03845f994e08248bf701c1ff0ccaa12e70f94251ec9272900f0f694e072b',
|
||||
'com.android.support:swiperefreshlayout:a3b41f7f6730866b49865e86e49f988d4858699765f534300fb2ff5f9325e712',
|
||||
'com.android.support:asynclayoutinflater:115bde87721f7334579b0c735f60dd7c98af1bb7f34010c5b0553b95dc351aa2',
|
||||
'android.arch.persistence.room:runtime:c21810eaafce370f1c9df1365393f55f962370a0d8b0b38b4771052c7021b737',
|
||||
'com.android.support:support-core-utils:c81e1e98ca3cb2edae002c69cf35b22aec364b8cb2f1042c97e206eb5790ac41',
|
||||
'com.android.support:loader:920b85efd72dc33e915b0f88a883fe73b88483c6df8751a741e17611f2460341',
|
||||
'com.android.support:support-vector-drawable:f658986d968172bccfed28578471c96050780fe5e133861e4d331069cc373f4d',
|
||||
'com.android.support:support-media-compat:266eff9605f515013eee1ebdbd8818a9270696dc807f34bbcc5fc11fb61a22c7',
|
||||
'com.android.support:support-compat:e17e3b01dbea3f9ea1c86943292f903ca93d2231c6242e456e0b6a9c5817118a',
|
||||
'com.android.support:versionedparcelable:60eb1cb08f71b65c3f6123135e03ebeb5930b5e126e1e5b2ac91b386908c9d02',
|
||||
'com.android.support:collections:93c258c8a09f531a267653829742c0f8f6da0e348b11cb8655b0855628f2d4f0',
|
||||
'android.arch.lifecycle:livedata:50ab0490c1ff1a7cfb4e554032998b080888946d0dd424f39900efc4a1bcd750',
|
||||
'android.arch.lifecycle:livedata-core:d6fdd8b985d6178d7ea2f16986a24e83f1bee936b74d43167c69e08d3cc12c50',
|
||||
'android.arch.lifecycle:runtime:c4e4be66c1b2f0abec593571454e1de14013f7e0f96bf2a9f212931a48cae550',
|
||||
'android.arch.lifecycle:common:8d378e88ebd5189e09eef623414812c868fd90aa519d6160e2311fb8b81cff56',
|
||||
'com.github.bumptech.glide:gifdecoder:59ccf3bb0cec11dab4b857382cbe0b171111b6fc62bf141adce4e1180889af15',
|
||||
'com.android.support:interpolator:7bc7ee86a0db39a4b51956f3e89842d2bd962118d57d779eb6ed6b34ba0677ea',
|
||||
'com.android.support:cursoradapter:87feffe742b8d62ca8a9833abe564838bf6a672e31c7ad1306ec4006adf90d21',
|
||||
'android.arch.persistence.room:common:7cf36bcd5f59ddc4876f887e36511bfd7b111f1eb717c0e9b6e2bcc710305ae6',
|
||||
'android.arch.persistence:db-framework:bd665448330acb90a6f551a87b0ba69169da2b8ec168b92f387997339cc14311',
|
||||
'android.arch.persistence:db:504e8c4307bfd53084924776ba3d49fed11b6f76d82dd80d5121c2d907fdfef6',
|
||||
'android.arch.core:runtime:c3215aa5873311b3f88a6f4e4a3c25ad89971bc127de8c3e1291c57f93a05c39',
|
||||
'android.arch.core:common:3a616a32f433e9e23f556b38575c31b013613d3ae85206263b7625fe1f4c151a',
|
||||
'android.arch.lifecycle:viewmodel:7de29cfaba77d6b5d5be234c57f6812d0150d087e63941af22ba1d1f8e2bc96a',
|
||||
'com.android.support:documentfile:47cdcd3e9302b7b064923f05487a5c03babbd9bbda4726b71e97791fab5d4779',
|
||||
'com.android.support:localbroadcastmanager:d287c823af5fdde72c099fcfc5f630efe9687af7a914343ae6fd92de32c8a806',
|
||||
'com.android.support:print:4be8a812d73e4a80e35b91ceae127def3f0bb9726bf3bc439aa0cc81503f5728',
|
||||
'com.android.support:support-annotations:5d5b9414f02d3fa0ee7526b8d5ddae0da67c8ecc8c4d63ffa6cf91488a93b927',
|
||||
'androidx.concurrent:futures:1f63078c41efd29d20ee3444fba93c6cdfaeeb862c6d3b6166ff8debd37d471a',
|
||||
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
|
||||
'org.whispersystems:signal-service-java:4db9adf763071756cfd93fe48a40850f684ca02f2dea59601841abba7715c5c1',
|
||||
'com.github.bumptech.glide:disklrucache:c1b1b6f5bbd01e2fcdc9d7f60913c8d338bdb65ed4a93bfa02b56f19daaade4b',
|
||||
'com.github.bumptech.glide:annotations:bede99ef9f71517a4274bac18fd3e483e9f2b6108d7d6fe8f4949be4aa4d9512',
|
||||
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
|
||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
|
||||
'com.google.android:flexbox:a9989fd13ae2ee42765dfc515fe362edf4f326e74925d02a10369df8092a4935',
|
||||
'org.jsoup:jsoup:abeaf34795a4de70f72aed6de5966d2955ec7eb348eeb813324f23c999575473',
|
||||
'com.google.guava:listenablefuture:e4ad7607e5c0477c6f890ef26a49cb8d1bb4dffb650bab4502afee64644e3069',
|
||||
'androidx.annotation:annotation:04f22f257944ce223701d5aa1bdc36fb7f4594e87b539044045cd161d965468e',
|
||||
'org.whispersystems:curve25519-android:82595394422b957d4a5b5f1b27b75ba25cf6dc4db4d312418ca38cd6fff279ca',
|
||||
'org.whispersystems:signal-protocol-java:5152c2b01a25147967d6bf82e540f947901bdfa79260be3eb3e96b03f787d6b5',
|
||||
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
|
||||
'com.googlecode.libphonenumber:libphonenumber:183392c0565be16d3f6f86680b4106bbde6fe31a402ad21bf9823d938c0c8706',
|
||||
'com.fasterxml.jackson.core:jackson-databind:835097bcdd11f5bc8a08378c70d4c8054dfa4b911691cc2752063c75534d198d',
|
||||
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
|
||||
'com.madgag.spongycastle:pkix:0d9cca6991f68eb373cfad309d5268c9fc38db5efb5fe00dcccf5c973af1eca1',
|
||||
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
|
||||
'org.threeten:threetenbp:f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7',
|
||||
'org.whispersystems:curve25519-java:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94',
|
||||
'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0',
|
||||
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
|
||||
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
|
||||
]
|
||||
}
|
||||
|
||||
android {
|
||||
flavorDimensions "none"
|
||||
compileSdkVersion 28
|
||||
|
@ -287,7 +169,7 @@ android {
|
|||
project.ext.set("archivesBaseName", "Signal");
|
||||
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
|
||||
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
|
||||
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
|
||||
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
|
||||
|
@ -296,6 +178,7 @@ android {
|
|||
buildConfigField "String", "USER_AGENT", "\"OWA\""
|
||||
buildConfigField "boolean", "DEV_BUILD", "false"
|
||||
buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
|
||||
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi", "armeabi-v7a", "x86"
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
|||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.service.LocalBackupListener;
|
||||
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
@ -207,6 +208,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||
RotateSignedPreKeyListener.schedule(this);
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
LocalBackupListener.schedule(this);
|
||||
RotateSenderCertificateListener.schedule(this);
|
||||
|
||||
if (BuildConfig.PLAY_STORE_DISABLED) {
|
||||
UpdateApkRefreshListener.schedule(this);
|
||||
|
|
|
@ -167,10 +167,11 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
|||
|
||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
||||
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
|
||||
messageRecord.getRecipientDeviceId(), "",
|
||||
messageRecord.getRecipientDeviceId(),
|
||||
messageRecord.getDateSent(),
|
||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||
!legacy ? Base64.decode(messageRecord.getBody()) : null);
|
||||
!legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||
0, null);
|
||||
|
||||
long pushId = pushDatabase.insert(envelope);
|
||||
|
||||
|
|
|
@ -51,10 +51,6 @@ import android.support.v7.app.AlertDialog;
|
|||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
import org.thoughtcrime.securesms.camera.CameraActivity;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
|
@ -79,6 +75,7 @@ import org.greenrobot.eventbus.Subscribe;
|
|||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.audio.AudioRecorder;
|
||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
|
||||
import org.thoughtcrime.securesms.camera.CameraActivity;
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
import org.thoughtcrime.securesms.components.AttachmentTypeSelector;
|
||||
|
@ -114,6 +111,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
||||
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
|
@ -129,6 +127,7 @@ import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
|||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
|
||||
import org.thoughtcrime.securesms.mms.AudioSlide;
|
||||
|
|
|
@ -303,13 +303,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new MessageDetailsLoader(this, getIntent().getStringExtra(TYPE_EXTRA),
|
||||
getIntent().getLongExtra(MESSAGE_ID_EXTRA, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||
MessageRecord messageRecord = getMessageRecord(this, cursor, getIntent().getStringExtra(TYPE_EXTRA));
|
||||
|
||||
if (messageRecord == null) {
|
||||
|
@ -320,7 +320,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||
recipientsList.setAdapter(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.text.TextUtils;
|
|||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -62,13 +61,16 @@ import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
|||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
|
@ -688,13 +690,17 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
|
|||
}
|
||||
|
||||
private void verifyAccount(@NonNull String code, @Nullable String pin) throws IOException {
|
||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(RegistrationActivity.this);
|
||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(RegistrationActivity.this);
|
||||
|
||||
TextSecurePreferences.setLocalRegistrationId(RegistrationActivity.this, registrationId);
|
||||
SessionUtil.archiveAllSessions(RegistrationActivity.this);
|
||||
|
||||
String signalingKey = Util.getSecret(52);
|
||||
|
||||
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, !registrationState.gcmToken.isPresent(), pin);
|
||||
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, !registrationState.gcmToken.isPresent(), pin,
|
||||
unidentifiedAccessKey, universalUnidentifiedAccess);
|
||||
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this);
|
||||
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(RegistrationActivity.this);
|
||||
|
@ -727,6 +733,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
|
|||
|
||||
private void handleSuccessfulRegistration() {
|
||||
ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new DirectoryRefreshJob(RegistrationActivity.this, false));
|
||||
ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new RotateCertificateJob(RegistrationActivity.this));
|
||||
|
||||
DirectoryRefreshListener.schedule(RegistrationActivity.this);
|
||||
RotateSignedPreKeyListener.schedule(RegistrationActivity.this);
|
||||
|
|
|
@ -83,7 +83,7 @@ public class PointerAttachment extends Attachment {
|
|||
pointer.get().asPointer().getSize().or(0),
|
||||
pointer.get().asPointer().getFileName().orNull(),
|
||||
String.valueOf(pointer.get().asPointer().getId()),
|
||||
encodedKey, pointer.get().asPointer().getRelay().orNull(),
|
||||
encodedKey, null,
|
||||
pointer.get().asPointer().getDigest().orNull(),
|
||||
pointer.get().asPointer().getVoiceNote(),
|
||||
pointer.get().asPointer().getWidth(),
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
package org.thoughtcrime.securesms.crypto;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import org.signal.libsignal.metadata.certificate.CertificateValidator;
|
||||
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.ecc.Curve;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UnidentifiedAccessUtil {
|
||||
|
||||
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
|
||||
|
||||
public static CertificateValidator getCertificateValidator() {
|
||||
try {
|
||||
ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BuildConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0);
|
||||
return new CertificateValidator(unidentifiedSenderTrustRoot);
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context,
|
||||
@NonNull Recipient recipient)
|
||||
{
|
||||
try {
|
||||
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
|
||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
||||
|
||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Their access key: " + (theirUnidentifiedAccessKey == null));
|
||||
Log.w(TAG, "Our access key: " + (ourUnidentifiedAccessKey == null));
|
||||
Log.w(TAG, "Our certificatE: " + (ourUnidentifiedAccessCertificate == null));
|
||||
|
||||
if (theirUnidentifiedAccessKey != null &&
|
||||
ourUnidentifiedAccessKey != null &&
|
||||
ourUnidentifiedAccessCertificate != null)
|
||||
{
|
||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(theirUnidentifiedAccessKey,
|
||||
ourUnidentifiedAccessCertificate),
|
||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
||||
ourUnidentifiedAccessCertificate)));
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
} catch (InvalidCertificateException e) {
|
||||
Log.w(TAG, e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
|
||||
try {
|
||||
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
|
||||
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);
|
||||
|
||||
if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) {
|
||||
ourUnidentifiedAccessKey = Util.getSecretBytes(16);
|
||||
}
|
||||
|
||||
if (ourUnidentifiedAccessKey != null && ourUnidentifiedAccessCertificate != null) {
|
||||
return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
||||
ourUnidentifiedAccessCertificate),
|
||||
new UnidentifiedAccess(ourUnidentifiedAccessKey,
|
||||
ourUnidentifiedAccessCertificate)));
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
} catch (InvalidCertificateException e) {
|
||||
Log.w(TAG, e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
public static @NonNull byte[] getSelfUnidentifiedAccessKey(@NonNull Context context) {
|
||||
return UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getProfileKey(context));
|
||||
}
|
||||
|
||||
private static @Nullable byte[] getTargetUnidentifiedAccessKey(@NonNull Recipient recipient) {
|
||||
byte[] theirProfileKey = recipient.resolve().getProfileKey();
|
||||
|
||||
switch (recipient.resolve().getUnidentifiedAccessMode()) {
|
||||
case UNKNOWN:
|
||||
if (theirProfileKey == null) return Util.getSecretBytes(16);
|
||||
else return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
case DISABLED:
|
||||
return null;
|
||||
case ENABLED:
|
||||
if (theirProfileKey == null) return null;
|
||||
else return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
|
||||
case UNRESTRICTED:
|
||||
return Util.getSecretBytes(16);
|
||||
default:
|
||||
throw new AssertionError("Unknown mode: " + recipient.getUnidentifiedAccessMode().getMode());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,6 +51,11 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
|||
return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getIdentity(SignalProtocolAddress address) {
|
||||
return identityKeyStore.getIdentity(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
||||
return preKeyStore.loadPreKey(preKeyId);
|
||||
|
|
|
@ -108,6 +108,17 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getIdentity(SignalProtocolAddress address) {
|
||||
Optional<IdentityRecord> record = DatabaseFactory.getIdentityDatabase(context).getIdentity(Address.fromSerialized(address.getName()));
|
||||
|
||||
if (record.isPresent()) {
|
||||
return record.get().getIdentityKey();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTrustedForSending(IdentityKey identityKey, Optional<IdentityRecord> identityRecord) {
|
||||
if (!identityRecord.isPresent()) {
|
||||
Log.w(TAG, "Nothing here, returning true...");
|
||||
|
|
|
@ -17,11 +17,12 @@ public class GroupReceiptDatabase extends Database {
|
|||
|
||||
public static final String TABLE_NAME = "group_receipts";
|
||||
|
||||
private static final String ID = "_id";
|
||||
public static final String MMS_ID = "mms_id";
|
||||
private static final String ADDRESS = "address";
|
||||
private static final String STATUS = "status";
|
||||
private static final String TIMESTAMP = "timestamp";
|
||||
private static final String ID = "_id";
|
||||
public static final String MMS_ID = "mms_id";
|
||||
private static final String ADDRESS = "address";
|
||||
private static final String STATUS = "status";
|
||||
private static final String TIMESTAMP = "timestamp";
|
||||
private static final String UNIDENTIFIED = "unidentified";
|
||||
|
||||
public static final int STATUS_UNKNOWN = -1;
|
||||
public static final int STATUS_UNDELIVERED = 0;
|
||||
|
@ -29,7 +30,7 @@ public class GroupReceiptDatabase extends Database {
|
|||
public static final int STATUS_READ = 2;
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
MMS_ID + " INTEGER, " + ADDRESS + " TEXT, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER);";
|
||||
MMS_ID + " INTEGER, " + ADDRESS + " TEXT, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXES = {
|
||||
"CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
|
||||
|
@ -63,6 +64,16 @@ public class GroupReceiptDatabase extends Database {
|
|||
new String[] {String.valueOf(mmsId), address.serialize(), String.valueOf(status)});
|
||||
}
|
||||
|
||||
public void setUnidentified(Address address, long mmsId, boolean unidentified) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(UNIDENTIFIED, unidentified ? 1 : 0);
|
||||
|
||||
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + ADDRESS + " = ?",
|
||||
new String[] {String.valueOf(mmsId), address.serialize()});
|
||||
|
||||
}
|
||||
|
||||
public @NonNull List<GroupReceiptInfo> getGroupReceiptInfo(long mmsId) {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
List<GroupReceiptInfo> results = new LinkedList<>();
|
||||
|
@ -71,7 +82,8 @@ public class GroupReceiptDatabase extends Database {
|
|||
while (cursor != null && cursor.moveToNext()) {
|
||||
results.add(new GroupReceiptInfo(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP))));
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,11 +104,13 @@ public class GroupReceiptDatabase extends Database {
|
|||
private final Address address;
|
||||
private final int status;
|
||||
private final long timestamp;
|
||||
private final boolean unidentified;
|
||||
|
||||
public GroupReceiptInfo(Address address, int status, long timestamp) {
|
||||
this.address = address;
|
||||
this.status = status;
|
||||
this.timestamp = timestamp;
|
||||
GroupReceiptInfo(Address address, int status, long timestamp, boolean unidentified) {
|
||||
this.address = address;
|
||||
this.status = status;
|
||||
this.timestamp = timestamp;
|
||||
this.unidentified = unidentified;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
|
@ -110,5 +124,9 @@ public class GroupReceiptDatabase extends Database {
|
|||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public boolean isUnidentified() {
|
||||
return unidentified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.net.Uri;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
@ -52,6 +51,7 @@ import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
|
|||
import org.thoughtcrime.securesms.database.model.Quote;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
||||
|
@ -125,7 +125,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " +
|
||||
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " +
|
||||
QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " +
|
||||
QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT);";
|
||||
QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
||||
|
@ -145,7 +145,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
MESSAGE_SIZE, STATUS, TRANSACTION_ID,
|
||||
BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
|
||||
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
|
||||
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, SHARED_CONTACTS,
|
||||
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, SHARED_CONTACTS, UNIDENTIFIED,
|
||||
"json_group_array(json_object(" +
|
||||
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
|
||||
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
|
||||
|
@ -403,6 +403,14 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
public void markUnidentified(long messageId, boolean unidentified) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markExpireStarted(long messageId) {
|
||||
markExpireStarted(messageId, System.currentTimeMillis());
|
||||
|
@ -575,6 +583,8 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
|
||||
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
|
||||
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
|
||||
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
|
||||
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
|
||||
|
||||
long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID));
|
||||
String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR));
|
||||
|
@ -585,20 +595,38 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
Set<Attachment> contactAttachments = new HashSet<>(Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList());
|
||||
List<Attachment> attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote).filterNot(contactAttachments::contains).map(a -> (Attachment)a).toList();
|
||||
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
|
||||
QuoteModel quote = null;
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
|
||||
List<NetworkFailure> networkFailures = new LinkedList<>();
|
||||
List<IdentityKeyMismatch> mismatches = new LinkedList<>();
|
||||
QuoteModel quote = null;
|
||||
|
||||
if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) {
|
||||
quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mismatchDocument)) {
|
||||
try {
|
||||
mismatches = JsonUtils.fromJson(mismatchDocument, IdentityKeyMismatchList.class).getList();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(networkDocument)) {
|
||||
try {
|
||||
networkFailures = JsonUtils.fromJson(networkDocument, NetworkFailureList.class).getList();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
|
||||
return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts);
|
||||
} else if (Types.isExpirationTimerUpdate(outboxType)) {
|
||||
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
|
||||
}
|
||||
|
||||
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts);
|
||||
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, networkFailures, mismatches);
|
||||
|
||||
if (Types.isSecureType(outboxType)) {
|
||||
return new OutgoingSecureMediaMessage(message);
|
||||
|
@ -730,6 +758,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
|
||||
contentValues.put(EXPIRES_IN, retrieved.getExpiresIn());
|
||||
contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0);
|
||||
contentValues.put(UNIDENTIFIED, retrieved.isUnidentified());
|
||||
|
||||
if (!contentValues.containsKey(DATE_SENT)) {
|
||||
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
|
||||
|
@ -1181,7 +1210,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
message.getOutgoingQuote().isOriginalMissing(),
|
||||
new SlideDeck(context, message.getOutgoingQuote().getAttachments())) :
|
||||
null,
|
||||
message.getSharedContacts());
|
||||
message.getSharedContacts(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1269,6 +1298,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
|
||||
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
|
||||
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
|
||||
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1;
|
||||
|
||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
readReceiptCount = 0;
|
||||
|
@ -1287,7 +1317,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount,
|
||||
threadId, body, slideDeck, partCount, box, mismatches,
|
||||
networkFailures, subscriptionId, expiresIn, expireStarted,
|
||||
readReceiptCount, quote, contacts);
|
||||
readReceiptCount, quote, contacts, unidentified);
|
||||
}
|
||||
|
||||
private Recipient getRecipientFor(String serialized) {
|
||||
|
|
|
@ -19,6 +19,7 @@ public interface MmsSmsColumns {
|
|||
public static final String EXPIRES_IN = "expires_in";
|
||||
public static final String EXPIRE_STARTED = "expire_started";
|
||||
public static final String NOTIFIED = "notified";
|
||||
public static final String UNIDENTIFIED = "unidentified";
|
||||
|
||||
public static class Types {
|
||||
protected static final long TOTAL_MASK = 0xFFFFFFFF;
|
||||
|
|
|
@ -49,7 +49,9 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||
MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX,
|
||||
SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
|
||||
SmsDatabase.STATUS,
|
||||
MmsSmsColumns.UNIDENTIFIED,
|
||||
MmsDatabase.PART_COUNT,
|
||||
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
|
||||
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
|
||||
MmsDatabase.STATUS,
|
||||
|
@ -232,6 +234,7 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
|
||||
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
|
||||
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
|
||||
MmsDatabase.UNIDENTIFIED,
|
||||
MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
|
||||
MmsSmsColumns.MISMATCHED_IDENTITIES,
|
||||
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
|
||||
|
@ -256,6 +259,7 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
|
||||
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
|
||||
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
|
||||
MmsDatabase.UNIDENTIFIED,
|
||||
MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
|
||||
MmsSmsColumns.MISMATCHED_IDENTITIES,
|
||||
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
|
||||
|
@ -266,7 +270,8 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsDatabase.QUOTE_BODY,
|
||||
MmsDatabase.QUOTE_MISSING,
|
||||
MmsDatabase.QUOTE_ATTACHMENT,
|
||||
MmsDatabase.SHARED_CONTACTS};
|
||||
MmsDatabase.SHARED_CONTACTS,
|
||||
MmsDatabase.UNIDENTIFIED};
|
||||
|
||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||
|
@ -304,6 +309,7 @@ public class MmsSmsDatabase extends Database {
|
|||
mmsColumnsPresent.add(MmsDatabase.EXPIRY);
|
||||
mmsColumnsPresent.add(MmsDatabase.NOTIFIED);
|
||||
mmsColumnsPresent.add(MmsDatabase.STATUS);
|
||||
mmsColumnsPresent.add(MmsDatabase.UNIDENTIFIED);
|
||||
mmsColumnsPresent.add(MmsDatabase.NETWORK_FAILURE);
|
||||
|
||||
mmsColumnsPresent.add(AttachmentDatabase.ROW_ID);
|
||||
|
@ -351,6 +357,7 @@ public class MmsSmsDatabase extends Database {
|
|||
smsColumnsPresent.add(SmsDatabase.DATE_SENT);
|
||||
smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED);
|
||||
smsColumnsPresent.add(SmsDatabase.STATUS);
|
||||
smsColumnsPresent.add(SmsDatabase.UNIDENTIFIED);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 4, MMS_TRANSPORT, selection, null, MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID, null);
|
||||
|
|
|
@ -20,17 +20,20 @@ public class PushDatabase extends Database {
|
|||
|
||||
private static final String TAG = PushDatabase.class.getSimpleName();
|
||||
|
||||
private static final String TABLE_NAME = "push";
|
||||
public static final String ID = "_id";
|
||||
public static final String TYPE = "type";
|
||||
public static final String SOURCE = "source";
|
||||
public static final String DEVICE_ID = "device_id";
|
||||
public static final String LEGACY_MSG = "body";
|
||||
public static final String CONTENT = "content";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
private static final String TABLE_NAME = "push";
|
||||
public static final String ID = "_id";
|
||||
public static final String TYPE = "type";
|
||||
public static final String SOURCE = "source";
|
||||
public static final String DEVICE_ID = "device_id";
|
||||
public static final String LEGACY_MSG = "body";
|
||||
public static final String CONTENT = "content";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String SERVER_TIMESTAMP = "server_timestamp";
|
||||
public static final String SERVER_GUID = "server_guid";
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER);";
|
||||
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
|
||||
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
|
||||
|
||||
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
|
@ -49,6 +52,8 @@ public class PushDatabase extends Database {
|
|||
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
|
||||
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
|
||||
values.put(TIMESTAMP, envelope.getTimestamp());
|
||||
values.put(SERVER_TIMESTAMP, envelope.getServerTimestamp());
|
||||
values.put(SERVER_GUID, envelope.getUuid());
|
||||
|
||||
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
|
||||
}
|
||||
|
@ -69,10 +74,11 @@ public class PushDatabase extends Database {
|
|||
return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
|
||||
"",
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
|
||||
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
|
||||
Util.isEmpty(content) ? null : Base64.decode(content));
|
||||
Util.isEmpty(content) ? null : Base64.decode(content),
|
||||
cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
@ -135,16 +141,19 @@ public class PushDatabase extends Database {
|
|||
if (cursor == null || !cursor.moveToNext())
|
||||
return null;
|
||||
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
||||
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
|
||||
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
||||
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
||||
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
||||
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
|
||||
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
|
||||
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
|
||||
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
|
||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
|
||||
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
|
||||
|
||||
return new SignalServiceEnvelope(type, source, deviceId, "", timestamp,
|
||||
return new SignalServiceEnvelope(type, source, deviceId, timestamp,
|
||||
legacyMessage != null ? Base64.decode(legacyMessage) : null,
|
||||
content != null ? Base64.decode(content) : null);
|
||||
content != null ? Base64.decode(content) : null,
|
||||
serverTimestamp, serverGuid);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
|
@ -14,6 +13,7 @@ import net.sqlcipher.database.SQLiteDatabase;
|
|||
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
@ -33,34 +33,36 @@ public class RecipientDatabase extends Database {
|
|||
|
||||
private static final String TAG = RecipientDatabase.class.getSimpleName();
|
||||
|
||||
static final String TABLE_NAME = "recipient_preferences";
|
||||
private static final String ID = "_id";
|
||||
static final String ADDRESS = "recipient_ids";
|
||||
private static final String BLOCK = "block";
|
||||
private static final String NOTIFICATION = "notification";
|
||||
private static final String VIBRATE = "vibrate";
|
||||
private static final String MUTE_UNTIL = "mute_until";
|
||||
private static final String COLOR = "color";
|
||||
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
|
||||
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
||||
private static final String EXPIRE_MESSAGES = "expire_messages";
|
||||
private static final String REGISTERED = "registered";
|
||||
private static final String PROFILE_KEY = "profile_key";
|
||||
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
||||
private static final String SYSTEM_PHOTO_URI = "system_contact_photo";
|
||||
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
|
||||
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||
private static final String PROFILE_SHARING = "profile_sharing_approval";
|
||||
private static final String CALL_RINGTONE = "call_ringtone";
|
||||
private static final String CALL_VIBRATE = "call_vibrate";
|
||||
private static final String NOTIFICATION_CHANNEL = "notification_channel";
|
||||
static final String TABLE_NAME = "recipient_preferences";
|
||||
private static final String ID = "_id";
|
||||
static final String ADDRESS = "recipient_ids";
|
||||
private static final String BLOCK = "block";
|
||||
private static final String NOTIFICATION = "notification";
|
||||
private static final String VIBRATE = "vibrate";
|
||||
private static final String MUTE_UNTIL = "mute_until";
|
||||
private static final String COLOR = "color";
|
||||
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
|
||||
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
||||
private static final String EXPIRE_MESSAGES = "expire_messages";
|
||||
private static final String REGISTERED = "registered";
|
||||
private static final String PROFILE_KEY = "profile_key";
|
||||
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
||||
private static final String SYSTEM_PHOTO_URI = "system_contact_photo";
|
||||
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
|
||||
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||
private static final String PROFILE_SHARING = "profile_sharing_approval";
|
||||
private static final String CALL_RINGTONE = "call_ringtone";
|
||||
private static final String CALL_VIBRATE = "call_vibrate";
|
||||
private static final String NOTIFICATION_CHANNEL = "notification_channel";
|
||||
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
||||
|
||||
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
||||
BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
|
||||
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
|
||||
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL
|
||||
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
||||
UNIDENTIFIED_ACCESS_MODE
|
||||
};
|
||||
|
||||
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||
|
@ -103,6 +105,24 @@ public class RecipientDatabase extends Database {
|
|||
}
|
||||
}
|
||||
|
||||
public enum UnidentifiedAccessMode {
|
||||
UNKNOWN(0), DISABLED(1), ENABLED(2), UNRESTRICTED(3);
|
||||
|
||||
private final int mode;
|
||||
|
||||
UnidentifiedAccessMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static UnidentifiedAccessMode fromMode(int mode) {
|
||||
return values()[mode];
|
||||
}
|
||||
}
|
||||
|
||||
public static final String CREATE_TABLE =
|
||||
"CREATE TABLE " + TABLE_NAME +
|
||||
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
|
@ -126,7 +146,8 @@ public class RecipientDatabase extends Database {
|
|||
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
||||
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
|
||||
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
|
||||
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL);";
|
||||
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
|
||||
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0);";
|
||||
|
||||
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
|
@ -169,29 +190,30 @@ public class RecipientDatabase extends Database {
|
|||
}
|
||||
|
||||
Optional<RecipientSettings> getRecipientSettings(@NonNull Cursor cursor) {
|
||||
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
|
||||
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
|
||||
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
|
||||
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
|
||||
int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE));
|
||||
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
|
||||
String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
|
||||
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
|
||||
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
|
||||
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
|
||||
int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
|
||||
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
|
||||
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
|
||||
String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI));
|
||||
String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL));
|
||||
String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI));
|
||||
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
|
||||
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
||||
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
|
||||
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
||||
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
|
||||
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
|
||||
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
|
||||
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
|
||||
int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE));
|
||||
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
|
||||
String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
|
||||
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
|
||||
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
|
||||
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
|
||||
int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
|
||||
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
|
||||
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
|
||||
String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI));
|
||||
String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL));
|
||||
String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI));
|
||||
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
|
||||
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
||||
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
|
||||
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
||||
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
||||
|
||||
MaterialColor color;
|
||||
byte[] profileKey = null;
|
||||
byte[] profileKey = null;
|
||||
|
||||
try {
|
||||
color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor);
|
||||
|
@ -219,7 +241,7 @@ public class RecipientDatabase extends Database {
|
|||
profileKey, systemDisplayName, systemContactPhoto,
|
||||
systemPhoneLabel, systemContactUri,
|
||||
signalProfileName, signalProfileAvatar, profileSharing,
|
||||
notificationChannel));
|
||||
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode)));
|
||||
}
|
||||
|
||||
public BulkOperationsHandle resetAllSystemContactInfo() {
|
||||
|
@ -309,6 +331,13 @@ public class RecipientDatabase extends Database {
|
|||
recipient.resolve().setExpireMessages(expiration);
|
||||
}
|
||||
|
||||
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
|
||||
}
|
||||
|
||||
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
||||
|
@ -506,26 +535,27 @@ public class RecipientDatabase extends Database {
|
|||
}
|
||||
|
||||
public static class RecipientSettings {
|
||||
private final boolean blocked;
|
||||
private final long muteUntil;
|
||||
private final VibrateState messageVibrateState;
|
||||
private final VibrateState callVibrateState;
|
||||
private final Uri messageRingtone;
|
||||
private final Uri callRingtone;
|
||||
private final MaterialColor color;
|
||||
private final boolean seenInviteReminder;
|
||||
private final int defaultSubscriptionId;
|
||||
private final int expireMessages;
|
||||
private final RegisteredState registered;
|
||||
private final byte[] profileKey;
|
||||
private final String systemDisplayName;
|
||||
private final String systemContactPhoto;
|
||||
private final String systemPhoneLabel;
|
||||
private final String systemContactUri;
|
||||
private final String signalProfileName;
|
||||
private final String signalProfileAvatar;
|
||||
private final boolean profileSharing;
|
||||
private final String notificationChannel;
|
||||
private final boolean blocked;
|
||||
private final long muteUntil;
|
||||
private final VibrateState messageVibrateState;
|
||||
private final VibrateState callVibrateState;
|
||||
private final Uri messageRingtone;
|
||||
private final Uri callRingtone;
|
||||
private final MaterialColor color;
|
||||
private final boolean seenInviteReminder;
|
||||
private final int defaultSubscriptionId;
|
||||
private final int expireMessages;
|
||||
private final RegisteredState registered;
|
||||
private final byte[] profileKey;
|
||||
private final String systemDisplayName;
|
||||
private final String systemContactPhoto;
|
||||
private final String systemPhoneLabel;
|
||||
private final String systemContactUri;
|
||||
private final String signalProfileName;
|
||||
private final String signalProfileAvatar;
|
||||
private final boolean profileSharing;
|
||||
private final String notificationChannel;
|
||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||
|
||||
RecipientSettings(boolean blocked, long muteUntil,
|
||||
@NonNull VibrateState messageVibrateState,
|
||||
|
@ -545,28 +575,30 @@ public class RecipientDatabase extends Database {
|
|||
@Nullable String signalProfileName,
|
||||
@Nullable String signalProfileAvatar,
|
||||
boolean profileSharing,
|
||||
@Nullable String notificationChannel)
|
||||
@Nullable String notificationChannel,
|
||||
@NonNull UnidentifiedAccessMode unidentifiedAccessMode)
|
||||
{
|
||||
this.blocked = blocked;
|
||||
this.muteUntil = muteUntil;
|
||||
this.messageVibrateState = messageVibrateState;
|
||||
this.callVibrateState = callVibrateState;
|
||||
this.messageRingtone = messageRingtone;
|
||||
this.callRingtone = callRingtone;
|
||||
this.color = color;
|
||||
this.seenInviteReminder = seenInviteReminder;
|
||||
this.defaultSubscriptionId = defaultSubscriptionId;
|
||||
this.expireMessages = expireMessages;
|
||||
this.registered = registered;
|
||||
this.profileKey = profileKey;
|
||||
this.systemDisplayName = systemDisplayName;
|
||||
this.systemContactPhoto = systemContactPhoto;
|
||||
this.systemPhoneLabel = systemPhoneLabel;
|
||||
this.systemContactUri = systemContactUri;
|
||||
this.signalProfileName = signalProfileName;
|
||||
this.signalProfileAvatar = signalProfileAvatar;
|
||||
this.profileSharing = profileSharing;
|
||||
this.notificationChannel = notificationChannel;
|
||||
this.blocked = blocked;
|
||||
this.muteUntil = muteUntil;
|
||||
this.messageVibrateState = messageVibrateState;
|
||||
this.callVibrateState = callVibrateState;
|
||||
this.messageRingtone = messageRingtone;
|
||||
this.callRingtone = callRingtone;
|
||||
this.color = color;
|
||||
this.seenInviteReminder = seenInviteReminder;
|
||||
this.defaultSubscriptionId = defaultSubscriptionId;
|
||||
this.expireMessages = expireMessages;
|
||||
this.registered = registered;
|
||||
this.profileKey = profileKey;
|
||||
this.systemDisplayName = systemDisplayName;
|
||||
this.systemContactPhoto = systemContactPhoto;
|
||||
this.systemPhoneLabel = systemPhoneLabel;
|
||||
this.systemContactUri = systemContactUri;
|
||||
this.signalProfileName = signalProfileName;
|
||||
this.signalProfileAvatar = signalProfileAvatar;
|
||||
this.profileSharing = profileSharing;
|
||||
this.notificationChannel = notificationChannel;
|
||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||
}
|
||||
|
||||
public @Nullable MaterialColor getColor() {
|
||||
|
@ -613,7 +645,7 @@ public class RecipientDatabase extends Database {
|
|||
return registered;
|
||||
}
|
||||
|
||||
public byte[] getProfileKey() {
|
||||
public @Nullable byte[] getProfileKey() {
|
||||
return profileKey;
|
||||
}
|
||||
|
||||
|
@ -648,6 +680,10 @@ public class RecipientDatabase extends Database {
|
|||
public @Nullable String getNotificationChannel() {
|
||||
return notificationChannel;
|
||||
}
|
||||
|
||||
public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() {
|
||||
return unidentifiedAccessMode;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RecipientReader implements Closeable {
|
||||
|
|
|
@ -22,7 +22,6 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
@ -38,6 +37,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
|
@ -81,7 +81,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " +
|
||||
MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
|
||||
EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " +
|
||||
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
|
||||
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
||||
|
@ -99,7 +99,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
PROTOCOL, READ, STATUS, TYPE,
|
||||
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT,
|
||||
MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED,
|
||||
NOTIFIED, READ_RECEIPT_COUNT
|
||||
NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED
|
||||
};
|
||||
|
||||
private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
|
||||
|
@ -243,6 +243,14 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE);
|
||||
}
|
||||
|
||||
public void markUnidentified(long id, boolean unidentified) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
|
||||
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markExpireStarted(long id) {
|
||||
markExpireStarted(id, System.currentTimeMillis());
|
||||
|
@ -559,6 +567,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
values.put(READ, unread ? 0 : 1);
|
||||
values.put(SUBSCRIPTION_ID, message.getSubscriptionId());
|
||||
values.put(EXPIRES_IN, message.getExpiresIn());
|
||||
values.put(UNIDENTIFIED, message.isUnidentified());
|
||||
|
||||
if (!TextUtils.isEmpty(message.getPseudoSubject()))
|
||||
values.put(SUBJECT, message.getPseudoSubject());
|
||||
|
@ -818,7 +827,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
|
||||
message.getSubscriptionId(), message.getExpiresIn(),
|
||||
System.currentTimeMillis(), 0);
|
||||
System.currentTimeMillis(), 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -858,6 +867,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
|
||||
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
|
||||
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
|
||||
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1;
|
||||
|
||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
readReceiptCount = 0;
|
||||
|
@ -871,7 +881,7 @@ public class SmsDatabase extends MessagingDatabase {
|
|||
addressDeviceId,
|
||||
dateSent, dateReceived, deliveryReceiptCount, type,
|
||||
threadId, status, mismatches, subscriptionId,
|
||||
expiresIn, expireStarted, readReceiptCount);
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified);
|
||||
}
|
||||
|
||||
private List<IdentityKeyMismatch> getMismatches(String document) {
|
||||
|
|
|
@ -56,8 +56,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
private static final int BAD_IMPORT_CLEANUP = 10;
|
||||
private static final int QUOTE_MISSING = 11;
|
||||
private static final int NOTIFICATION_CHANNELS = 12;
|
||||
private static final int SECRET_SENDER = 13;
|
||||
|
||||
private static final int DATABASE_VERSION = 12;
|
||||
private static final int DATABASE_VERSION = 13;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
|
@ -284,6 +285,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
}
|
||||
|
||||
if (oldVersion < SECRET_SENDER) {
|
||||
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0");
|
||||
db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
|
@ -55,11 +55,12 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
|
|||
List<IdentityKeyMismatch> mismatches,
|
||||
List<NetworkFailure> failures, int subscriptionId,
|
||||
long expiresIn, long expireStarted, int readReceiptCount,
|
||||
@Nullable Quote quote, @Nullable List<Contact> contacts)
|
||||
@Nullable Quote quote, @Nullable List<Contact> contacts,
|
||||
boolean unidentified)
|
||||
{
|
||||
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
|
||||
dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
|
||||
subscriptionId, expiresIn, expireStarted, slideDeck, readReceiptCount, quote, contacts);
|
||||
subscriptionId, expiresIn, expireStarted, slideDeck, readReceiptCount, quote, contacts, unidentified);
|
||||
|
||||
this.context = context.getApplicationContext();
|
||||
this.partCount = partCount;
|
||||
|
|
|
@ -53,6 +53,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
private final int subscriptionId;
|
||||
private final long expiresIn;
|
||||
private final long expireStarted;
|
||||
private final boolean unidentified;
|
||||
|
||||
MessageRecord(Context context, long id, String body, Recipient conversationRecipient,
|
||||
Recipient individualRecipient, int recipientDeviceId,
|
||||
|
@ -61,7 +62,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
List<IdentityKeyMismatch> mismatches,
|
||||
List<NetworkFailure> networkFailures,
|
||||
int subscriptionId, long expiresIn, long expireStarted,
|
||||
int readReceiptCount)
|
||||
int readReceiptCount, boolean unidentified)
|
||||
{
|
||||
super(context, body, conversationRecipient, dateSent, dateReceived,
|
||||
threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount);
|
||||
|
@ -73,6 +74,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
this.subscriptionId = subscriptionId;
|
||||
this.expiresIn = expiresIn;
|
||||
this.expireStarted = expireStarted;
|
||||
this.unidentified = unidentified;
|
||||
}
|
||||
|
||||
public abstract boolean isMms();
|
||||
|
@ -242,4 +244,8 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
public long getExpireStarted() {
|
||||
return expireStarted;
|
||||
}
|
||||
|
||||
public boolean isUnidentified() {
|
||||
return unidentified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,9 @@ public abstract class MmsMessageRecord extends MessageRecord {
|
|||
long type, List<IdentityKeyMismatch> mismatches,
|
||||
List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn,
|
||||
long expireStarted, @NonNull SlideDeck slideDeck, int readReceiptCount,
|
||||
@Nullable Quote quote, @NonNull List<Contact> contacts)
|
||||
@Nullable Quote quote, @NonNull List<Contact> contacts, boolean unidentified)
|
||||
{
|
||||
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount);
|
||||
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified);
|
||||
|
||||
this.slideDeck = slideDeck;
|
||||
this.quote = quote;
|
||||
|
|
|
@ -56,7 +56,7 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
|
|||
super(context, id, "", conversationRecipient, individualRecipient, recipientDeviceId,
|
||||
dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
|
||||
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId,
|
||||
0, 0, slideDeck, readReceiptCount, null, Collections.emptyList());
|
||||
0, 0, slideDeck, readReceiptCount, null, Collections.emptyList(), false);
|
||||
|
||||
this.contentLocation = contentLocation;
|
||||
this.messageSize = messageSize;
|
||||
|
|
|
@ -47,12 +47,12 @@ public class SmsMessageRecord extends MessageRecord {
|
|||
long type, long threadId,
|
||||
int status, List<IdentityKeyMismatch> mismatches,
|
||||
int subscriptionId, long expiresIn, long expireStarted,
|
||||
int readReceiptCount)
|
||||
int readReceiptCount, boolean unidentified)
|
||||
{
|
||||
super(context, id, body, recipient, individualRecipient, recipientDeviceId,
|
||||
dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
|
||||
mismatches, new LinkedList<>(), subscriptionId,
|
||||
expiresIn, expireStarted, readReceiptCount);
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified);
|
||||
}
|
||||
|
||||
public long getType() {
|
||||
|
|
|
@ -33,7 +33,9 @@ import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
||||
|
@ -83,7 +85,9 @@ import dagger.Provides;
|
|||
SendReadReceiptJob.class,
|
||||
MultiDeviceReadReceiptUpdateJob.class,
|
||||
AppProtectionPreferenceFragment.class,
|
||||
GcmBroadcastReceiver.class})
|
||||
GcmBroadcastReceiver.class,
|
||||
RotateCertificateJob.class,
|
||||
SendDeliveryReceiptJob.class})
|
||||
public class SignalCommunicationModule {
|
||||
|
||||
private static final String TAG = SignalCommunicationModule.class.getSimpleName();
|
||||
|
@ -118,10 +122,13 @@ public class SignalCommunicationModule {
|
|||
new DynamicCredentialsProvider(context),
|
||||
new SignalProtocolStoreImpl(context),
|
||||
BuildConfig.USER_AGENT,
|
||||
TextSecurePreferences.isMultiDevice(context),
|
||||
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
||||
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
|
||||
Optional.of(new SecurityEventListener(context)));
|
||||
} else {
|
||||
this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe());
|
||||
this.messageSender.setMessagePipe(IncomingMessageObserver.getPipe(), IncomingMessageObserver.getUnidentifiedPipe());
|
||||
this.messageSender.setIsMultiDevice(TextSecurePreferences.isMultiDevice(context));
|
||||
}
|
||||
|
||||
return this.messageSender;
|
||||
|
|
|
@ -4,7 +4,6 @@ package org.thoughtcrime.securesms.groups;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
|
@ -17,6 +16,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
|
|||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
|
@ -27,8 +27,8 @@ import org.thoughtcrime.securesms.util.Base64;
|
|||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||
|
||||
|
@ -47,7 +47,7 @@ public class GroupMessageProcessor {
|
|||
private static final String TAG = GroupMessageProcessor.class.getSimpleName();
|
||||
|
||||
public static @Nullable Long process(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
boolean outgoing)
|
||||
{
|
||||
|
@ -62,13 +62,13 @@ public class GroupMessageProcessor {
|
|||
Optional<GroupRecord> record = database.getGroup(id);
|
||||
|
||||
if (record.isPresent() && group.getType() == Type.UPDATE) {
|
||||
return handleGroupUpdate(context, envelope, group, record.get(), outgoing);
|
||||
return handleGroupUpdate(context, content, group, record.get(), outgoing);
|
||||
} else if (!record.isPresent() && group.getType() == Type.UPDATE) {
|
||||
return handleGroupCreate(context, envelope, group, outgoing);
|
||||
return handleGroupCreate(context, content, group, outgoing);
|
||||
} else if (record.isPresent() && group.getType() == Type.QUIT) {
|
||||
return handleGroupLeave(context, envelope, group, record.get(), outgoing);
|
||||
return handleGroupLeave(context, content, group, record.get(), outgoing);
|
||||
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
|
||||
return handleGroupInfoRequest(context, envelope, group, record.get());
|
||||
return handleGroupInfoRequest(context, content, group, record.get());
|
||||
} else {
|
||||
Log.w(TAG, "Received unknown type, ignoring...");
|
||||
return null;
|
||||
|
@ -76,7 +76,7 @@ public class GroupMessageProcessor {
|
|||
}
|
||||
|
||||
private static @Nullable Long handleGroupCreate(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
boolean outgoing)
|
||||
{
|
||||
|
@ -95,14 +95,13 @@ public class GroupMessageProcessor {
|
|||
}
|
||||
|
||||
database.create(id, group.getName().orNull(), members,
|
||||
avatar != null && avatar.isPointer() ? avatar.asPointer() : null,
|
||||
envelope.getRelay());
|
||||
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null);
|
||||
|
||||
return storeMessage(context, envelope, group, builder.build(), outgoing);
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
private static @Nullable Long handleGroupUpdate(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord groupRecord,
|
||||
boolean outgoing)
|
||||
|
@ -156,25 +155,25 @@ public class GroupMessageProcessor {
|
|||
|
||||
if (!groupRecord.isActive()) database.setActive(id, true);
|
||||
|
||||
return storeMessage(context, envelope, group, builder.build(), outgoing);
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
private static Long handleGroupInfoRequest(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord record)
|
||||
{
|
||||
if (record.getMembers().contains(Address.fromExternal(context, envelope.getSource()))) {
|
||||
if (record.getMembers().contains(Address.fromExternal(context, content.getSender()))) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new PushGroupUpdateJob(context, envelope.getSource(), group.getGroupId()));
|
||||
.add(new PushGroupUpdateJob(context, content.getSender(), group.getGroupId()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Long handleGroupLeave(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord record,
|
||||
boolean outgoing)
|
||||
|
@ -186,11 +185,11 @@ public class GroupMessageProcessor {
|
|||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.QUIT);
|
||||
|
||||
if (members.contains(Address.fromExternal(context, envelope.getSource()))) {
|
||||
database.remove(id, Address.fromExternal(context, envelope.getSource()));
|
||||
if (members.contains(Address.fromExternal(context, content.getSender()))) {
|
||||
database.remove(id, Address.fromExternal(context, content.getSender()));
|
||||
if (outgoing) database.setActive(id, false);
|
||||
|
||||
return storeMessage(context, envelope, group, builder.build(), outgoing);
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -198,7 +197,7 @@ public class GroupMessageProcessor {
|
|||
|
||||
|
||||
private static @Nullable Long storeMessage(@NonNull Context context,
|
||||
@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupContext storage,
|
||||
boolean outgoing)
|
||||
|
@ -213,7 +212,7 @@ public class GroupMessageProcessor {
|
|||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false));
|
||||
Recipient recipient = Recipient.from(context, addres, false);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, envelope.getTimestamp(), 0, null, Collections.emptyList());
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, null, Collections.emptyList());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
|
||||
|
@ -223,7 +222,7 @@ public class GroupMessageProcessor {
|
|||
} else {
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
String body = Base64.encodeBytes(storage.toByteArray());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
|
||||
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
|
||||
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||
|
|
|
@ -193,7 +193,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
|||
Log.i(TAG, "Downloading attachment with no digest...");
|
||||
}
|
||||
|
||||
return new SignalServiceAttachmentPointer(id, null, key, relay,
|
||||
return new SignalServiceAttachmentPointer(id, null, key,
|
||||
Optional.of(Util.toIntExact(attachment.getSize())),
|
||||
Optional.absent(),
|
||||
0, 0,
|
||||
|
|
|
@ -98,7 +98,7 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
|
|||
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
|
||||
attachment.deleteOnExit();
|
||||
|
||||
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false);
|
||||
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false);
|
||||
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
|
||||
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500);
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ public class MmsDownloadJob extends MasterSecretJob {
|
|||
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), true)));
|
||||
}
|
||||
|
||||
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false);
|
||||
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false);
|
||||
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader;
|
||||
|
@ -30,6 +31,7 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName();
|
||||
|
||||
@Inject transient SignalServiceMessageSender messageSender;
|
||||
|
@ -75,7 +77,8 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
|
|||
}
|
||||
}
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
|||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
|
@ -238,7 +239,8 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
|
|||
.build();
|
||||
|
||||
try {
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
} catch (IOException ioe) {
|
||||
throw new NetworkException(ioe);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
|
@ -131,7 +132,8 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
|
|||
.withLength(contactsFile.length())
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
@ -86,7 +87,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
|
|||
|
||||
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
|
||||
|
||||
messageSender.sendMessage(syncMessage);
|
||||
messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@ package org.thoughtcrime.securesms.jobs;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
|
@ -58,7 +59,8 @@ public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements Injec
|
|||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.jobmanager.SafeData;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
|
@ -100,7 +101,8 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
|
|||
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
|
||||
}
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages),
|
||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,11 +7,14 @@ import android.support.annotation.NonNull;
|
|||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
|
@ -101,7 +104,8 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
|
|||
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
|
||||
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
|
||||
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
|
||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -7,11 +8,18 @@ import android.os.Build;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
|
||||
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
|
||||
import org.signal.libsignal.metadata.ProtocolDuplicateMessageException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidVersionException;
|
||||
import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
|
||||
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
||||
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -22,6 +30,7 @@ import org.thoughtcrime.securesms.contactshare.Contact;
|
|||
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
|
@ -40,6 +49,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
||||
|
@ -53,25 +64,13 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.DuplicateMessageException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||
import org.whispersystems.libsignal.InvalidMessageException;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.libsignal.LegacyMessageException;
|
||||
import org.whispersystems.libsignal.NoSessionException;
|
||||
import org.whispersystems.libsignal.UntrustedIdentityException;
|
||||
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
|
||||
import org.whispersystems.libsignal.state.SessionStore;
|
||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
@ -212,11 +211,11 @@ public class PushDecryptJob extends ContextJob {
|
|||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore);
|
||||
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
|
||||
|
||||
SignalServiceContent content = cipher.decrypt(envelope);
|
||||
|
||||
if (shouldIgnore(envelope, content)) {
|
||||
if (shouldIgnore(content)) {
|
||||
Log.i(TAG, "Ignoring message.");
|
||||
return;
|
||||
}
|
||||
|
@ -225,41 +224,45 @@ public class PushDecryptJob extends ContextJob {
|
|||
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent();
|
||||
|
||||
if (message.isEndSession()) handleEndSessionMessage(envelope, message, smsMessageId);
|
||||
else if (message.isGroupUpdate()) handleGroupMessage(envelope, message, smsMessageId);
|
||||
else if (message.isExpirationUpdate()) handleExpirationUpdate(envelope, message, smsMessageId);
|
||||
else if (isMediaMessage) handleMediaMessage(envelope, message, smsMessageId);
|
||||
else if (message.getBody().isPresent()) handleTextMessage(envelope, message, smsMessageId);
|
||||
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
||||
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
||||
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId);
|
||||
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId);
|
||||
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId);
|
||||
|
||||
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
|
||||
handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
|
||||
handleUnknownGroupMessage(content, message.getGroupInfo().get());
|
||||
}
|
||||
|
||||
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
|
||||
handleProfileKey(envelope, message);
|
||||
handleProfileKey(content, message);
|
||||
}
|
||||
|
||||
if (content.isNeedsReceipt()) {
|
||||
handleNeedsDeliveryReceipt(content, message);
|
||||
}
|
||||
} else if (content.getSyncMessage().isPresent()) {
|
||||
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
||||
|
||||
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(envelope, syncMessage.getSent().get());
|
||||
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
|
||||
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
|
||||
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), envelope.getTimestamp());
|
||||
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
|
||||
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
||||
else Log.w(TAG, "Contains no known sync types...");
|
||||
} else if (content.getCallMessage().isPresent()) {
|
||||
Log.i(TAG, "Got call message...");
|
||||
SignalServiceCallMessage message = content.getCallMessage().get();
|
||||
|
||||
if (message.getOfferMessage().isPresent()) handleCallOfferMessage(envelope, message.getOfferMessage().get(), smsMessageId);
|
||||
else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(envelope, message.getAnswerMessage().get());
|
||||
else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(envelope, message.getIceUpdateMessages().get());
|
||||
else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(envelope, message.getHangupMessage().get(), smsMessageId);
|
||||
else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(envelope, message.getBusyMessage().get());
|
||||
if (message.getOfferMessage().isPresent()) handleCallOfferMessage(content, message.getOfferMessage().get(), smsMessageId);
|
||||
else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(content, message.getAnswerMessage().get());
|
||||
else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(content, message.getIceUpdateMessages().get());
|
||||
else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(content, message.getHangupMessage().get(), smsMessageId);
|
||||
else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(content, message.getBusyMessage().get());
|
||||
} else if (content.getReceiptMessage().isPresent()) {
|
||||
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
|
||||
|
||||
if (message.isReadReceipt()) handleReadReceipt(envelope, message);
|
||||
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(envelope, message);
|
||||
if (message.isReadReceipt()) handleReadReceipt(content, message);
|
||||
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message);
|
||||
} else {
|
||||
Log.w(TAG, "Got unrecognized message...");
|
||||
}
|
||||
|
@ -267,28 +270,30 @@ public class PushDecryptJob extends ContextJob {
|
|||
if (envelope.isPreKeySignalMessage()) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob(context));
|
||||
}
|
||||
} catch (InvalidVersionException e) {
|
||||
} catch (ProtocolInvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
handleInvalidVersionMessage(envelope, smsMessageId);
|
||||
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
|
||||
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
||||
Log.w(TAG, e);
|
||||
handleCorruptMessage(envelope, smsMessageId);
|
||||
} catch (NoSessionException e) {
|
||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (StorageFailedException e) {
|
||||
Log.w(TAG, e);
|
||||
handleNoSessionMessage(envelope, smsMessageId);
|
||||
} catch (LegacyMessageException e) {
|
||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (ProtocolNoSessionException e) {
|
||||
Log.w(TAG, e);
|
||||
handleLegacyMessage(envelope, smsMessageId);
|
||||
} catch (DuplicateMessageException e) {
|
||||
handleNoSessionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (ProtocolLegacyMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleDuplicateMessage(envelope, smsMessageId);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
handleLegacyMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (ProtocolDuplicateMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||
} catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
handleUntrustedIdentityMessage(envelope, smsMessageId);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCallOfferMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCallOfferMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull OfferMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
|
@ -301,29 +306,29 @@ public class PushDecryptJob extends ContextJob {
|
|||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_INCOMING_CALL);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, envelope.getTimestamp());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent);
|
||||
else context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCallAnswerMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCallAnswerMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull AnswerMessage message)
|
||||
{
|
||||
Log.i(TAG, "handleCallAnswerMessage...");
|
||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_RESPONSE_MESSAGE);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_DESCRIPTION, message.getDescription());
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
private void handleCallIceUpdateMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCallIceUpdateMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull List<IceUpdateMessage> messages)
|
||||
{
|
||||
Log.w(TAG, "handleCallIceUpdateMessage... " + messages.size());
|
||||
|
@ -331,7 +336,7 @@ public class PushDecryptJob extends ContextJob {
|
|||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_ICE_MESSAGE);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP, message.getSdp());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_MID, message.getSdpMid());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_ICE_SDP_LINE_INDEX, message.getSdpMLineIndex());
|
||||
|
@ -340,7 +345,7 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleCallHangupMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCallHangupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull HangupMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
|
@ -351,32 +356,32 @@ public class PushDecryptJob extends ContextJob {
|
|||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_REMOTE_HANGUP);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCallBusyMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCallBusyMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull BusyMessage message)
|
||||
{
|
||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
intent.setAction(WebRtcCallService.ACTION_REMOTE_BUSY);
|
||||
intent.putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId());
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, envelope.getSource()));
|
||||
intent.putExtra(WebRtcCallService.EXTRA_REMOTE_ADDRESS, Address.fromExternal(context, content.getSender()));
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
private void handleEndSessionMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
private void handleEndSessionMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
|
||||
envelope.getSourceDevice(),
|
||||
message.getTimestamp(),
|
||||
"", Optional.absent(), 0);
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
|
||||
content.getSenderDevice(),
|
||||
content.getTimestamp(),
|
||||
"", Optional.absent(), 0,
|
||||
content.isNeedsReceipt());
|
||||
|
||||
Long threadId;
|
||||
|
||||
|
@ -393,7 +398,7 @@ public class PushDecryptJob extends ContextJob {
|
|||
|
||||
if (threadId != null) {
|
||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||
sessionStore.deleteAllSessions(envelope.getSource());
|
||||
sessionStore.deleteAllSessions(content.getSender());
|
||||
|
||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||
MessageNotifier.updateNotification(context, threadId);
|
||||
|
@ -424,15 +429,15 @@ public class PushDecryptJob extends ContextJob {
|
|||
return threadId;
|
||||
}
|
||||
|
||||
private void handleGroupMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleGroupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
throws MmsException
|
||||
throws StorageFailedException
|
||||
{
|
||||
GroupMessageProcessor.process(context, envelope, message, false);
|
||||
GroupMessageProcessor.process(context, content, message, false);
|
||||
|
||||
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(envelope, message).getExpireMessages()) {
|
||||
handleExpirationUpdate(envelope, message, Optional.absent());
|
||||
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
|
||||
handleExpirationUpdate(content, message, Optional.absent());
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
|
@ -440,36 +445,41 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleUnknownGroupMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleUnknownGroupMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group)
|
||||
{
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RequestGroupInfoJob(context, envelope.getSource(), group.getGroupId()));
|
||||
.add(new RequestGroupInfoJob(context, content.getSender(), group.getGroupId()));
|
||||
}
|
||||
|
||||
private void handleExpirationUpdate(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
throws MmsException
|
||||
throws StorageFailedException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipient recipient = getMessageDestination(envelope, message);
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
|
||||
message.getTimestamp(), -1,
|
||||
message.getExpiresInSeconds() * 1000L, true,
|
||||
Optional.fromNullable(envelope.getRelay()),
|
||||
Optional.absent(), message.getGroupInfo(),
|
||||
Optional.absent(), Optional.absent(), Optional.absent());
|
||||
try {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipient recipient = getMessageDestination(content, message);
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
|
||||
message.getTimestamp(), -1,
|
||||
message.getExpiresInSeconds() * 1000L, true,
|
||||
content.isNeedsReceipt(),
|
||||
Optional.absent(),
|
||||
message.getGroupInfo(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent());
|
||||
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
|
||||
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
}
|
||||
} catch (MmsException e) {
|
||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,48 +487,53 @@ public class PushDecryptJob extends ContextJob {
|
|||
IdentityUtil.processVerifiedMessage(context, verifiedMessage);
|
||||
}
|
||||
|
||||
private void handleSynchronizeSentMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SentTranscriptMessage message)
|
||||
throws MmsException
|
||||
throws StorageFailedException
|
||||
|
||||
{
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
try {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
|
||||
Long threadId;
|
||||
Long threadId;
|
||||
|
||||
if (message.getMessage().isEndSession()) {
|
||||
threadId = handleSynchronizeSentEndSessionMessage(message);
|
||||
} else if (message.getMessage().isGroupUpdate()) {
|
||||
threadId = GroupMessageProcessor.process(context, envelope, message.getMessage(), true);
|
||||
} else if (message.getMessage().isExpirationUpdate()) {
|
||||
threadId = handleSynchronizeSentExpirationUpdate(message);
|
||||
} else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent()) {
|
||||
threadId = handleSynchronizeSentMediaMessage(message);
|
||||
} else {
|
||||
threadId = handleSynchronizeSentTextMessage(message);
|
||||
}
|
||||
|
||||
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
|
||||
handleUnknownGroupMessage(envelope, message.getMessage().getGroupInfo().get());
|
||||
}
|
||||
|
||||
if (message.getMessage().getProfileKey().isPresent()) {
|
||||
Recipient recipient = null;
|
||||
|
||||
if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
|
||||
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
|
||||
|
||||
|
||||
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||
if (message.getMessage().isEndSession()) {
|
||||
threadId = handleSynchronizeSentEndSessionMessage(message);
|
||||
} else if (message.getMessage().isGroupUpdate()) {
|
||||
threadId = GroupMessageProcessor.process(context, content, message.getMessage(), true);
|
||||
} else if (message.getMessage().isExpirationUpdate()) {
|
||||
threadId = handleSynchronizeSentExpirationUpdate(message);
|
||||
} else if (message.getMessage().getAttachments().isPresent() || message.getMessage().getQuote().isPresent()) {
|
||||
threadId = handleSynchronizeSentMediaMessage(message);
|
||||
} else {
|
||||
threadId = handleSynchronizeSentTextMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (threadId != null) {
|
||||
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
|
||||
MessageNotifier.updateNotification(getContext());
|
||||
}
|
||||
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
|
||||
handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get());
|
||||
}
|
||||
|
||||
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
||||
if (message.getMessage().getProfileKey().isPresent()) {
|
||||
Recipient recipient = null;
|
||||
|
||||
if (message.getDestination().isPresent()) recipient = Recipient.from(context, Address.fromExternal(context, message.getDestination().get()), false);
|
||||
else if (message.getMessage().getGroupInfo().isPresent()) recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false)), false);
|
||||
|
||||
|
||||
if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (threadId != null) {
|
||||
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
|
||||
MessageNotifier.updateNotification(getContext());
|
||||
}
|
||||
|
||||
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
||||
} catch (MmsException e) {
|
||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSynchronizeRequestMessage(@NonNull RequestMessage message)
|
||||
|
@ -572,51 +587,48 @@ public class PushDecryptJob extends ContextJob {
|
|||
MessageNotifier.updateNotification(context);
|
||||
}
|
||||
|
||||
private void handleMediaMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleMediaMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
throws MmsException
|
||||
throws StorageFailedException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipient recipient = getMessageDestination(envelope, message);
|
||||
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
||||
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
|
||||
message.getTimestamp(), -1,
|
||||
message.getExpiresInSeconds() * 1000L, false,
|
||||
Optional.fromNullable(envelope.getRelay()),
|
||||
message.getBody(),
|
||||
message.getGroupInfo(),
|
||||
message.getAttachments(),
|
||||
quote,
|
||||
sharedContacts);
|
||||
try {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
||||
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()),
|
||||
message.getTimestamp(), -1,
|
||||
message.getExpiresInSeconds() * 1000L, false,
|
||||
content.isNeedsReceipt(),
|
||||
message.getBody(),
|
||||
message.getGroupInfo(),
|
||||
message.getAttachments(),
|
||||
quote,
|
||||
sharedContacts);
|
||||
|
||||
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
|
||||
handleExpirationUpdate(envelope, message, Optional.absent());
|
||||
}
|
||||
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
|
||||
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
if (insertResult.isPresent()) {
|
||||
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
|
||||
for (DatabaseAttachment attachment : attachments) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
|
||||
}
|
||||
|
||||
for (DatabaseAttachment attachment : attachments) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
}
|
||||
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
}
|
||||
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
} catch (MmsException e) {
|
||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
||||
}
|
||||
}
|
||||
|
||||
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
|
||||
throws MmsException
|
||||
{
|
||||
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipient recipient = getSyncMessageDestination(message);
|
||||
|
||||
|
@ -646,7 +658,8 @@ public class PushDecryptJob extends ContextJob {
|
|||
message.getTimestamp(), -1,
|
||||
message.getMessage().getExpiresInSeconds() * 1000,
|
||||
ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(),
|
||||
sharedContacts.or(Collections.emptyList()));
|
||||
sharedContacts.or(Collections.emptyList()),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
|
||||
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
|
||||
|
||||
|
@ -677,17 +690,17 @@ public class PushDecryptJob extends ContextJob {
|
|||
return threadId;
|
||||
}
|
||||
|
||||
private void handleTextMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleTextMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
throws MmsException
|
||||
throws StorageFailedException
|
||||
{
|
||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||
String body = message.getBody().isPresent() ? message.getBody().get() : "";
|
||||
Recipient recipient = getMessageDestination(envelope, message);
|
||||
Recipient recipient = getMessageDestination(content, message);
|
||||
|
||||
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
|
||||
handleExpirationUpdate(envelope, message, Optional.absent());
|
||||
handleExpirationUpdate(content, message, Optional.absent());
|
||||
}
|
||||
|
||||
Long threadId;
|
||||
|
@ -695,11 +708,12 @@ public class PushDecryptJob extends ContextJob {
|
|||
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
|
||||
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
|
||||
} else {
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
|
||||
envelope.getSourceDevice(),
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, content.getSender()),
|
||||
content.getSenderDevice(),
|
||||
message.getTimestamp(), body,
|
||||
message.getGroupInfo(),
|
||||
message.getExpiresInSeconds() * 1000L);
|
||||
message.getExpiresInSeconds() * 1000L,
|
||||
content.isNeedsReceipt());
|
||||
|
||||
textMessage = new IncomingEncryptedMessage(textMessage, body);
|
||||
Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage);
|
||||
|
@ -758,13 +772,13 @@ public class PushDecryptJob extends ContextJob {
|
|||
return threadId;
|
||||
}
|
||||
|
||||
private void handleInvalidVersionMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleInvalidVersionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
|
||||
|
@ -775,13 +789,13 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleCorruptMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
|
||||
|
@ -792,13 +806,13 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleNoSessionMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
|
||||
|
@ -809,13 +823,13 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleLegacyMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
|
||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
|
||||
|
@ -827,7 +841,7 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleDuplicateMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleDuplicateMessage(@NonNull String sender, int senderDeviceId, long timestamp,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
// Let's start ignoring these now
|
||||
|
@ -842,45 +856,11 @@ public class PushDecryptJob extends ContextJob {
|
|||
// }
|
||||
}
|
||||
|
||||
private void handleUntrustedIdentityMessage(@NonNull SignalServiceEnvelope envelope,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
{
|
||||
try {
|
||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
|
||||
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
|
||||
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
|
||||
IdentityKey identityKey = whisperMessage.getIdentityKey();
|
||||
String encoded = Base64.encodeBytes(serialized);
|
||||
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress,
|
||||
envelope.getSourceDevice(),
|
||||
envelope.getTimestamp(), encoded,
|
||||
Optional.absent(), 0);
|
||||
|
||||
if (!smsMessageId.isPresent()) {
|
||||
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage());
|
||||
Optional<InsertResult> insertResult = database.insertMessageInbox(bundleMessage);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey);
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
database.updateMessageBody(smsMessageId.get(), encoded);
|
||||
database.markAsPreKeyBundle(smsMessageId.get());
|
||||
database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey);
|
||||
}
|
||||
} catch (InvalidMessageException | InvalidVersionException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleProfileKey(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleProfileKey(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message)
|
||||
{
|
||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
||||
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
|
||||
Address sourceAddress = Address.fromExternal(context, content.getSender());
|
||||
Recipient recipient = Recipient.from(context, sourceAddress, false);
|
||||
|
||||
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
||||
|
@ -889,17 +869,27 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleDeliveryReceipt(@NonNull SignalServiceEnvelope envelope,
|
||||
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message)
|
||||
{
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new SendDeliveryReceiptJob(context, Address.fromExternal(context, content.getSender()), message.getTimestamp()));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceReceiptMessage message)
|
||||
{
|
||||
for (long timestamp : message.getTimestamps()) {
|
||||
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
|
||||
DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp), System.currentTimeMillis());
|
||||
.incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReadReceipt(@NonNull SignalServiceEnvelope envelope,
|
||||
@SuppressLint("DefaultLocale")
|
||||
private void handleReadReceipt(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceReceiptMessage message)
|
||||
{
|
||||
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
|
@ -907,7 +897,7 @@ public class PushDecryptJob extends ContextJob {
|
|||
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
|
||||
|
||||
DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp), envelope.getTimestamp());
|
||||
.incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, content.getSender()), timestamp), content.getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -960,12 +950,11 @@ public class PushDecryptJob extends ContextJob {
|
|||
return Optional.of(contacts);
|
||||
}
|
||||
|
||||
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
|
||||
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) {
|
||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
|
||||
envelope.getSourceDevice(),
|
||||
envelope.getTimestamp(), "",
|
||||
Optional.absent(), 0);
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, sender),
|
||||
senderDevice, timestamp, "",
|
||||
Optional.absent(), 0, false);
|
||||
|
||||
textMessage = new IncomingEncryptedMessage(textMessage, "");
|
||||
return database.insertMessageInbox(textMessage);
|
||||
|
@ -979,21 +968,20 @@ public class PushDecryptJob extends ContextJob {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private Recipient getMessageDestination(SignalServiceEnvelope envelope, SignalServiceDataMessage message) {
|
||||
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
||||
if (message.getGroupInfo().isPresent()) {
|
||||
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
|
||||
} else {
|
||||
return Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false);
|
||||
return Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldIgnore(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
||||
Recipient sender = Recipient.from(context, Address.fromExternal(context, envelope.getSource()), false);
|
||||
private boolean shouldIgnore(@NonNull SignalServiceContent content) {
|
||||
Recipient sender = Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
|
||||
|
||||
if (content.getDataMessage().isPresent()) {
|
||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||
Recipient conversation = getMessageDestination(envelope, message);
|
||||
Recipient conversation = getMessageDestination(content, message);
|
||||
|
||||
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
|
||||
return true;
|
||||
|
@ -1023,4 +1011,25 @@ public class PushDecryptJob extends ContextJob {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
private static class StorageFailedException extends Exception {
|
||||
private final String sender;
|
||||
private final int senderDevice;
|
||||
|
||||
private StorageFailedException(Exception e, String sender, int senderDevice) {
|
||||
super(e);
|
||||
this.sender = sender;
|
||||
this.senderDevice = senderDevice;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public int getSenderDevice() {
|
||||
return senderDevice;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,15 +4,18 @@ import android.content.Context;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
|
@ -24,25 +27,27 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
|||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -96,47 +101,56 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||
|
||||
@Override
|
||||
public void onPushSend()
|
||||
throws MmsException, IOException, NoSuchMessageException
|
||||
throws IOException, MmsException, NoSuchMessageException, RetryLaterException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
|
||||
List<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
|
||||
List<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
|
||||
|
||||
try {
|
||||
Log.i(TAG, "Sending message: " + messageId);
|
||||
List<Address> target;
|
||||
|
||||
deliver(message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
|
||||
if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress));
|
||||
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
|
||||
else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId);
|
||||
|
||||
database.markAsSent(messageId, true);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
List<SendMessageResult> results = deliver(message, target);
|
||||
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
|
||||
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
|
||||
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
|
||||
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
|
||||
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
|
||||
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
||||
|
||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||
database.markExpireStarted(messageId);
|
||||
ApplicationContext.getInstance(context)
|
||||
.getExpiringMessageManager()
|
||||
.scheduleDeletion(messageId, true, message.getExpiresIn());
|
||||
for (NetworkFailure resolvedFailure : resolvedNetworkFailures) {
|
||||
database.removeFailure(messageId, resolvedFailure);
|
||||
existingNetworkFailures.remove(resolvedFailure);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Sent message: " + messageId);
|
||||
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
database.markAsSentFailed(messageId);
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
} catch (EncapsulatedExceptions e) {
|
||||
Log.w(TAG, e);
|
||||
List<NetworkFailure> failures = new LinkedList<>();
|
||||
|
||||
for (NetworkFailureException nfe : e.getNetworkExceptions()) {
|
||||
failures.add(new NetworkFailure(Address.fromSerialized(nfe.getE164number())));
|
||||
for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) {
|
||||
database.removeMismatchedIdentity(messageId, resolvedIdentity.getAddress(), resolvedIdentity.getIdentityKey());
|
||||
existingIdentityMismatches.remove(resolvedIdentity);
|
||||
}
|
||||
|
||||
for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) {
|
||||
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
|
||||
if (!networkFailures.isEmpty()) {
|
||||
database.addFailures(messageId, networkFailures);
|
||||
}
|
||||
|
||||
database.addFailures(messageId, failures);
|
||||
for (IdentityKeyMismatch mismatch : identityMismatches) {
|
||||
database.addMismatchedIdentity(messageId, mismatch.getAddress(), mismatch.getIdentityKey());
|
||||
}
|
||||
|
||||
if (e.getNetworkExceptions().isEmpty() && e.getUntrustedIdentityExceptions().isEmpty()) {
|
||||
for (SendMessageResult success : successes) {
|
||||
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()),
|
||||
messageId,
|
||||
success.getSuccess().isUnidentified());
|
||||
}
|
||||
|
||||
if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
|
||||
database.markAsSent(messageId, true);
|
||||
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
|
||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||
|
@ -145,16 +159,28 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||
.getExpiringMessageManager()
|
||||
.scheduleDeletion(messageId, true, message.getExpiresIn());
|
||||
}
|
||||
} else {
|
||||
} else if (!networkFailures.isEmpty()) {
|
||||
throw new RetryLaterException();
|
||||
} else if (!identityMismatches.isEmpty()) {
|
||||
database.markAsSentFailed(messageId);
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
}
|
||||
|
||||
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
database.markAsSentFailed(messageId);
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
Log.w(TAG, e);
|
||||
database.markAsSentFailed(messageId);
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetryThrowable(Exception exception) {
|
||||
if (exception instanceof IOException) return true;
|
||||
if (exception instanceof IOException) return true;
|
||||
if (exception instanceof RetryLaterException) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -163,23 +189,24 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
||||
}
|
||||
|
||||
private void deliver(OutgoingMediaMessage message, @Nullable Address filterAddress)
|
||||
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations)
|
||||
throws IOException, RecipientFormattingException, InvalidNumberException,
|
||||
EncapsulatedExceptions, UndeliverableMessageException
|
||||
UndeliverableMessageException, UntrustedIdentityException
|
||||
{
|
||||
String groupId = message.getRecipient().getAddress().toGroupString();
|
||||
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
|
||||
List<Address> recipients = getGroupMessageRecipients(groupId, messageId);
|
||||
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
||||
List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
|
||||
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
|
||||
Optional<Quote> quote = getQuoteFor(message);
|
||||
List<SharedContact> sharedContacts = getSharedContactsFor(message);
|
||||
List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList();
|
||||
|
||||
List<SignalServiceAddress> addresses;
|
||||
|
||||
if (filterAddress != null) addresses = getPushAddresses(filterAddress);
|
||||
else addresses = getPushAddresses(recipients);
|
||||
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
|
||||
.map(address -> Address.fromSerialized(address.getNumber()))
|
||||
.map(address -> Recipient.from(context, address, false))
|
||||
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
|
||||
.toList();
|
||||
|
||||
if (message.isGroup()) {
|
||||
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
|
||||
|
@ -193,7 +220,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||
.asGroupMessage(group)
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(addresses, groupDataMessage);
|
||||
return messageSender.sendMessage(addresses, unidentifiedAccess, groupDataMessage);
|
||||
} else {
|
||||
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId));
|
||||
SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
|
||||
|
@ -208,20 +235,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||
.withSharedContacts(sharedContacts)
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(addresses, groupMessage);
|
||||
return messageSender.sendMessage(addresses, unidentifiedAccess, groupMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private List<SignalServiceAddress> getPushAddresses(Address address) {
|
||||
List<SignalServiceAddress> addresses = new LinkedList<>();
|
||||
addresses.add(getPushAddress(address));
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private List<SignalServiceAddress> getPushAddresses(List<Address> addresses) {
|
||||
return Stream.of(addresses).map(this::getPushAddress).toList();
|
||||
}
|
||||
|
||||
private @NonNull List<Address> getGroupMessageRecipients(String groupId, long messageId) {
|
||||
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
|
||||
if (!destinations.isEmpty()) return Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList();
|
||||
|
|
|
@ -4,15 +4,15 @@ package org.thoughtcrime.securesms.jobs;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
@ -122,7 +122,9 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
|
|||
.withExpiration(groupRecipient.getExpireMessages())
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(new SignalServiceAddress(source), message);
|
||||
messageSender.sendMessage(new SignalServiceAddress(source),
|
||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)),
|
||||
message);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,16 +3,16 @@ package org.thoughtcrime.securesms.jobs;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
|
@ -80,9 +80,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||
try {
|
||||
Log.i(TAG, "Sending message: " + messageId);
|
||||
|
||||
deliver(message);
|
||||
boolean unidentified = deliver(message);
|
||||
database.markAsSent(messageId, true);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
database.markUnidentified(messageId, unidentified);
|
||||
|
||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||
database.markExpireStarted(messageId);
|
||||
|
@ -117,7 +118,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
}
|
||||
|
||||
private void deliver(OutgoingMediaMessage message)
|
||||
private boolean deliver(OutgoingMediaMessage message)
|
||||
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
|
||||
UndeliverableMessageException
|
||||
{
|
||||
|
@ -144,7 +145,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||
.asExpirationUpdate(message.isExpirationUpdate())
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(address, mediaMessage);
|
||||
return messageSender.sendMessage(address, UnidentifiedAccessUtil.getAccessFor(context, message.getRecipient()), mediaMessage).getSuccess().isUnidentified();
|
||||
} catch (UnregisteredUserException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new InsecureFallbackApprovalException(e);
|
||||
|
|
|
@ -4,15 +4,12 @@ import android.content.Context;
|
|||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
|
@ -25,17 +26,19 @@ public abstract class PushReceivedJob extends ContextJob {
|
|||
|
||||
public void processEnvelope(@NonNull SignalServiceEnvelope envelope) {
|
||||
synchronized (RECEIVE_LOCK) {
|
||||
Address source = Address.fromExternal(context, envelope.getSource());
|
||||
Recipient recipient = Recipient.from(context, source, false);
|
||||
if (envelope.hasSource()) {
|
||||
Address source = Address.fromExternal(context, envelope.getSource());
|
||||
Recipient recipient = Recipient.from(context, source, false);
|
||||
|
||||
if (!isActiveNumber(recipient)) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
|
||||
if (!isActiveNumber(recipient)) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (envelope.isReceipt()) {
|
||||
handleReceipt(envelope);
|
||||
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
|
||||
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender()) {
|
||||
handleMessage(envelope);
|
||||
} else {
|
||||
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
|
||||
|
@ -47,6 +50,7 @@ public abstract class PushReceivedJob extends ContextJob {
|
|||
new PushDecryptJob(context).processMessage(envelope);
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private void handleReceipt(SignalServiceEnvelope envelope) {
|
||||
Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()),
|
||||
|
|
|
@ -92,7 +92,6 @@ public abstract class PushSendJob extends SendJob {
|
|||
}
|
||||
|
||||
protected SignalServiceAddress getPushAddress(Address address) {
|
||||
// String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString());
|
||||
String relay = null;
|
||||
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.jobmanager.SafeData;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
|
@ -20,7 +21,9 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
|||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
|
@ -76,8 +79,9 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||
try {
|
||||
Log.i(TAG, "Sending message: " + messageId);
|
||||
|
||||
deliver(record);
|
||||
boolean unidentified = deliver(record);
|
||||
database.markAsSent(messageId, true);
|
||||
database.markUnidentified(messageId, unidentified);
|
||||
|
||||
if (record.getExpiresIn() > 0) {
|
||||
database.markExpireStarted(messageId);
|
||||
|
@ -118,22 +122,25 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||
}
|
||||
}
|
||||
|
||||
private void deliver(SmsMessageRecord message)
|
||||
private boolean deliver(SmsMessageRecord message)
|
||||
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException
|
||||
{
|
||||
try {
|
||||
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
|
||||
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
|
||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(message.getDateSent())
|
||||
.withBody(message.getBody())
|
||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||
.withProfileKey(profileKey.orNull())
|
||||
.asEndSessionMessage(message.isEndSession())
|
||||
.build();
|
||||
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
|
||||
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient());
|
||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, message.getIndividualRecipient());
|
||||
|
||||
Log.w(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
||||
|
||||
messageSender.sendMessage(address, textSecureMessage);
|
||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(message.getDateSent())
|
||||
.withBody(message.getBody())
|
||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||
.withProfileKey(profileKey.orNull())
|
||||
.asEndSessionMessage(message.isEndSession())
|
||||
.build();
|
||||
|
||||
return messageSender.sendMessage(address, unidentifiedAccess, textSecureMessage).getSuccess().isUnidentified();
|
||||
} catch (UnregisteredUserException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new InsecureFallbackApprovalException(e);
|
||||
|
|
|
@ -8,6 +8,12 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
|
||||
|
@ -48,12 +54,15 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
|
|||
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
String signalingKey = TextSecurePreferences.getSignalingKey(context);
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
|
||||
boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context);
|
||||
String pin = TextSecurePreferences.getRegistrationLockPin(context);
|
||||
String signalingKey = TextSecurePreferences.getSignalingKey(context);
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
|
||||
boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context);
|
||||
String pin = TextSecurePreferences.getRegistrationLockPin(context);
|
||||
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
|
||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
||||
|
||||
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin);
|
||||
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin,
|
||||
unidentifiedAccessKey, universalUnidentifiedAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,10 +3,14 @@ package org.thoughtcrime.securesms.jobs;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
|
@ -23,6 +27,7 @@ import androidx.work.Data;
|
|||
|
||||
public class RequestGroupInfoJob extends ContextJob implements InjectableType {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = RequestGroupInfoJob.class.getSimpleName();
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
@ -77,7 +82,9 @@ public class RequestGroupInfoJob extends ContextJob implements InjectableType {
|
|||
.withTimestamp(System.currentTimeMillis())
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(new SignalServiceAddress(source), message);
|
||||
messageSender.sendMessage(new SignalServiceAddress(source),
|
||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)),
|
||||
message);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,14 +5,16 @@ import android.content.Context;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
@ -20,11 +22,15 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
|
|||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -88,12 +94,26 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||
private void handleIndividualRecipient(Recipient recipient)
|
||||
throws IOException, InvalidKeyException, InvalidNumberException
|
||||
{
|
||||
String number = recipient.getAddress().toPhoneString();
|
||||
SignalServiceProfile profile = retrieveProfile(number);
|
||||
String number = recipient.getAddress().toPhoneString();
|
||||
Optional<UnidentifiedAccess> unidentifiedAccess = getUnidentifiedAccess(recipient);
|
||||
|
||||
SignalServiceProfile profile;
|
||||
|
||||
try {
|
||||
profile = retrieveProfile(number, unidentifiedAccess);
|
||||
} catch (AuthorizationFailedException e) {
|
||||
if (unidentifiedAccess.isPresent()) {
|
||||
// XXX Update UI
|
||||
profile = retrieveProfile(number, Optional.absent());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
setIdentityKey(recipient, profile.getIdentityKey());
|
||||
setProfileName(recipient, profile.getName());
|
||||
setProfileAvatar(recipient, profile.getAvatar());
|
||||
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
|
||||
}
|
||||
|
||||
private void handleGroupRecipient(Recipient group)
|
||||
|
@ -106,18 +126,20 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||
}
|
||||
}
|
||||
|
||||
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
|
||||
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
|
||||
throws IOException
|
||||
{
|
||||
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
|
||||
|
||||
if (pipe != null) {
|
||||
try {
|
||||
return pipe.getProfile(new SignalServiceAddress(number));
|
||||
return pipe.getProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
return receiver.retrieveProfile(new SignalServiceAddress(number));
|
||||
return receiver.retrieveProfile(new SignalServiceAddress(number), unidentifiedAccess);
|
||||
}
|
||||
|
||||
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
|
||||
|
@ -143,6 +165,30 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||
}
|
||||
}
|
||||
|
||||
private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) {
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
byte[] profileKey = recipient.getProfileKey();
|
||||
|
||||
// XXX Update UI
|
||||
if (unrestrictedUnidentifiedAccess) {
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
|
||||
} else if (profileKey == null || unidentifiedAccessVerifier == null) {
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
|
||||
} else {
|
||||
ProfileCipher profileCipher = new ProfileCipher(profileKey);
|
||||
boolean verifiedUnidentifiedAccess;
|
||||
|
||||
try {
|
||||
verifiedUnidentifiedAccess = profileCipher.verifyUnidentifiedAccess(Base64.decode(unidentifiedAccessVerifier));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
verifiedUnidentifiedAccess = false;
|
||||
}
|
||||
|
||||
recipientDatabase.setUnidentifiedAccessMode(recipient, verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
private void setProfileName(Recipient recipient, String profileName) {
|
||||
try {
|
||||
byte[] profileKey = recipient.getProfileKey();
|
||||
|
@ -172,4 +218,14 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||
.add(new RetrieveProfileAvatarJob(context, recipient, profileAvatar));
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<UnidentifiedAccess> getUnidentifiedAccess(@NonNull Recipient recipient) {
|
||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
||||
|
||||
if (unidentifiedAccess.isPresent()) {
|
||||
return unidentifiedAccess.get().getTargetUnidentifiedAccess();
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class RotateCertificateJob extends ContextJob implements InjectableType {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String TAG = RotateCertificateJob.class.getName();
|
||||
|
||||
@Inject transient SignalServiceAccountManager accountManager;
|
||||
|
||||
public RotateCertificateJob() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public RotateCertificateJob(Context context) {
|
||||
super(context, JobParameters.newBuilder()
|
||||
.withGroupId("__ROTATE_SENDER_CERTIFICATE__")
|
||||
.withNetworkRequirement()
|
||||
.create());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data serialize(@NonNull Data.Builder dataBuilder) {
|
||||
return dataBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(@NonNull SafeData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded() {}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
byte[] certificate = accountManager.getSenderCertificate();
|
||||
TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception e) {
|
||||
return e instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
Log.w(TAG, "Failed to rotate sender certificate!");
|
||||
}
|
||||
}
|
100
src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java
Normal file
100
src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java
Normal file
|
@ -0,0 +1,100 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.work.Data;
|
||||
|
||||
public class SendDeliveryReceiptJob extends ContextJob implements InjectableType {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String KEY_ADDRESS = "address";
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
private static final String KEY_TIMESTAMP = "timestamp";
|
||||
|
||||
private static final String TAG = SendReadReceiptJob.class.getSimpleName();
|
||||
|
||||
@Inject
|
||||
transient SignalServiceMessageSender messageSender;
|
||||
|
||||
private String address;
|
||||
private long messageId;
|
||||
private long timestamp;
|
||||
|
||||
public SendDeliveryReceiptJob() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public SendDeliveryReceiptJob(Context context, Address address, long messageId) {
|
||||
super(context, JobParameters.newBuilder()
|
||||
.withNetworkRequirement()
|
||||
.create());
|
||||
|
||||
this.address = address.serialize();
|
||||
this.messageId = messageId;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded() {}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data serialize(@NonNull Data.Builder dataBuilder) {
|
||||
return dataBuilder.putString(KEY_ADDRESS, address)
|
||||
.putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putLong(KEY_TIMESTAMP, timestamp)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(@NonNull SafeData data) {
|
||||
this.address = data.getString(KEY_ADDRESS);
|
||||
this.messageId = data.getLong(KEY_MESSAGE_ID);
|
||||
this.timestamp = data.getLong(KEY_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException, UntrustedIdentityException {
|
||||
SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
|
||||
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
|
||||
Collections.singletonList(messageId),
|
||||
timestamp);
|
||||
|
||||
messageSender.sendReceipt(remoteAddress,
|
||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
|
||||
receiptMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception e) {
|
||||
if (e instanceof PushNetworkException) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
Log.w(TAG, "Failed to send delivery receipt to: " + address);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,11 +4,13 @@ package org.thoughtcrime.securesms.jobs;
|
|||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobParameters;
|
||||
import org.thoughtcrime.securesms.jobmanager.SafeData;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||
|
@ -86,7 +88,9 @@ public class SendReadReceiptJob extends ContextJob implements InjectableType {
|
|||
SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
|
||||
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
|
||||
|
||||
messageSender.sendReceipt(remoteAddress, receiptMessage);
|
||||
messageSender.sendReceipt(remoteAddress,
|
||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
|
||||
receiptMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ public class IncomingMediaMessage {
|
|||
private final long expiresIn;
|
||||
private final boolean expirationUpdate;
|
||||
private final QuoteModel quote;
|
||||
private final boolean unidentified;
|
||||
|
||||
private final List<Attachment> attachments = new LinkedList<>();
|
||||
private final List<Contact> sharedContacts = new LinkedList<>();
|
||||
|
@ -35,7 +36,8 @@ public class IncomingMediaMessage {
|
|||
List<Attachment> attachments,
|
||||
int subscriptionId,
|
||||
long expiresIn,
|
||||
boolean expirationUpdate)
|
||||
boolean expirationUpdate,
|
||||
boolean unidentified)
|
||||
{
|
||||
this.from = from;
|
||||
this.groupId = groupId.orNull();
|
||||
|
@ -46,6 +48,7 @@ public class IncomingMediaMessage {
|
|||
this.expiresIn = expiresIn;
|
||||
this.expirationUpdate = expirationUpdate;
|
||||
this.quote = null;
|
||||
this.unidentified = unidentified;
|
||||
|
||||
this.attachments.addAll(attachments);
|
||||
}
|
||||
|
@ -55,7 +58,7 @@ public class IncomingMediaMessage {
|
|||
int subscriptionId,
|
||||
long expiresIn,
|
||||
boolean expirationUpdate,
|
||||
Optional<String> relay,
|
||||
boolean unidentified,
|
||||
Optional<String> body,
|
||||
Optional<SignalServiceGroup> group,
|
||||
Optional<List<SignalServiceAttachment>> attachments,
|
||||
|
@ -70,6 +73,7 @@ public class IncomingMediaMessage {
|
|||
this.expiresIn = expiresIn;
|
||||
this.expirationUpdate = expirationUpdate;
|
||||
this.quote = quote.orNull();
|
||||
this.unidentified = unidentified;
|
||||
|
||||
if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId(), false));
|
||||
else this.groupId = null;
|
||||
|
@ -125,4 +129,8 @@ public class IncomingMediaMessage {
|
|||
public List<Contact> getSharedContacts() {
|
||||
return sharedContacts;
|
||||
}
|
||||
|
||||
public boolean isUnidentified() {
|
||||
return unidentified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import android.text.TextUtils;
|
|||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
@ -13,32 +15,40 @@ import java.util.List;
|
|||
|
||||
public class OutgoingMediaMessage {
|
||||
|
||||
private final Recipient recipient;
|
||||
protected final String body;
|
||||
protected final List<Attachment> attachments;
|
||||
private final long sentTimeMillis;
|
||||
private final int distributionType;
|
||||
private final int subscriptionId;
|
||||
private final long expiresIn;
|
||||
private final QuoteModel outgoingQuote;
|
||||
private final List<Contact> contacts = new LinkedList<>();
|
||||
private final Recipient recipient;
|
||||
protected final String body;
|
||||
protected final List<Attachment> attachments;
|
||||
private final long sentTimeMillis;
|
||||
private final int distributionType;
|
||||
private final int subscriptionId;
|
||||
private final long expiresIn;
|
||||
private final QuoteModel outgoingQuote;
|
||||
|
||||
private final List<NetworkFailure> networkFailures = new LinkedList<>();
|
||||
private final List<IdentityKeyMismatch> identityKeyMismatches = new LinkedList<>();
|
||||
private final List<Contact> contacts = new LinkedList<>();
|
||||
|
||||
public OutgoingMediaMessage(Recipient recipient, String message,
|
||||
List<Attachment> attachments, long sentTimeMillis,
|
||||
int subscriptionId, long expiresIn,
|
||||
int distributionType, @Nullable QuoteModel outgoingQuote,
|
||||
@NonNull List<Contact> contacts)
|
||||
int distributionType,
|
||||
@Nullable QuoteModel outgoingQuote,
|
||||
@NonNull List<Contact> contacts,
|
||||
@NonNull List<NetworkFailure> networkFailures,
|
||||
@NonNull List<IdentityKeyMismatch> identityKeyMismatches)
|
||||
{
|
||||
this.recipient = recipient;
|
||||
this.body = message;
|
||||
this.sentTimeMillis = sentTimeMillis;
|
||||
this.distributionType = distributionType;
|
||||
this.attachments = attachments;
|
||||
this.subscriptionId = subscriptionId;
|
||||
this.expiresIn = expiresIn;
|
||||
this.outgoingQuote = outgoingQuote;
|
||||
this.recipient = recipient;
|
||||
this.body = message;
|
||||
this.sentTimeMillis = sentTimeMillis;
|
||||
this.distributionType = distributionType;
|
||||
this.attachments = attachments;
|
||||
this.subscriptionId = subscriptionId;
|
||||
this.expiresIn = expiresIn;
|
||||
this.outgoingQuote = outgoingQuote;
|
||||
|
||||
this.contacts.addAll(contacts);
|
||||
this.networkFailures.addAll(networkFailures);
|
||||
this.identityKeyMismatches.addAll(identityKeyMismatches);
|
||||
}
|
||||
|
||||
public OutgoingMediaMessage(Recipient recipient, SlideDeck slideDeck, String message, long sentTimeMillis, int subscriptionId, long expiresIn, int distributionType, @Nullable QuoteModel outgoingQuote, @NonNull List<Contact> contacts)
|
||||
|
@ -47,7 +57,8 @@ public class OutgoingMediaMessage {
|
|||
buildMessage(slideDeck, message),
|
||||
slideDeck.asAttachments(),
|
||||
sentTimeMillis, subscriptionId,
|
||||
expiresIn, distributionType, outgoingQuote, contacts);
|
||||
expiresIn, distributionType, outgoingQuote,
|
||||
contacts, new LinkedList<>(), new LinkedList<>());
|
||||
}
|
||||
|
||||
public OutgoingMediaMessage(OutgoingMediaMessage that) {
|
||||
|
@ -60,6 +71,8 @@ public class OutgoingMediaMessage {
|
|||
this.expiresIn = that.expiresIn;
|
||||
this.outgoingQuote = that.outgoingQuote;
|
||||
|
||||
this.identityKeyMismatches.addAll(that.identityKeyMismatches);
|
||||
this.networkFailures.addAll(that.networkFailures);
|
||||
this.contacts.addAll(that.contacts);
|
||||
}
|
||||
|
||||
|
@ -111,6 +124,14 @@ public class OutgoingMediaMessage {
|
|||
return contacts;
|
||||
}
|
||||
|
||||
public @NonNull List<NetworkFailure> getNetworkFailures() {
|
||||
return networkFailures;
|
||||
}
|
||||
|
||||
public @NonNull List<IdentityKeyMismatch> getIdentityKeyMismatches() {
|
||||
return identityKeyMismatches;
|
||||
}
|
||||
|
||||
private static String buildMessage(SlideDeck slideDeck, String message) {
|
||||
if (!TextUtils.isEmpty(message) && !TextUtils.isEmpty(slideDeck.getBody())) {
|
||||
return slideDeck.getBody() + "\n\n" + message;
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.attachments.Attachment;
|
|||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
|
||||
|
@ -19,7 +20,7 @@ public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
|
|||
@Nullable QuoteModel quote,
|
||||
@NonNull List<Contact> contacts)
|
||||
{
|
||||
super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, distributionType, quote, contacts);
|
||||
super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, distributionType, quote, contacts, Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public OutgoingSecureMediaMessage(OutgoingMediaMessage base) {
|
||||
|
|
|
@ -76,7 +76,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
|
|||
|
||||
if (recipient.isGroupRecipient()) {
|
||||
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
|
||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList());
|
||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||
replyThreadId = MessageSender.send(context, reply, threadId, false, null);
|
||||
} else {
|
||||
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
|
||||
|
|
|
@ -72,7 +72,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
|||
long expiresIn = recipient.getExpireMessages() * 1000L;
|
||||
|
||||
if (recipient.isGroupRecipient()) {
|
||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList());
|
||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipient, responseText.toString(), new LinkedList<>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
|
||||
threadId = MessageSender.send(context, reply, -1, false, null);
|
||||
} else if (TextSecurePreferences.isPushRegistered(context) && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
OutgoingEncryptedMessage reply = new OutgoingEncryptedMessage(recipient, responseText.toString(), expiresIn);
|
||||
|
|
|
@ -3,6 +3,10 @@ package org.thoughtcrime.securesms.push;
|
|||
import android.content.Context;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
|
||||
|
@ -20,4 +24,12 @@ public class SecurityEventListener implements SignalServiceMessageSender.EventLi
|
|||
public void onSecurityEvent(SignalServiceAddress textSecureAddress) {
|
||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnidentifiedAccessFailed(SignalServiceAddress address) {
|
||||
// XXX Update UI
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setUnidentifiedAccessMode(Recipient.from(context, Address.fromSerialized(address.getNumber()), false),
|
||||
RecipientDatabase.UnidentifiedAccessMode.DISABLED);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import okhttp3.TlsVersion;
|
|||
|
||||
public class SignalServiceNetworkAccess {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = SignalServiceNetworkAccess.class.getName();
|
||||
|
||||
private static final String COUNTRY_CODE_EGYPT = "+20";
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.database.Address;
|
|||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
|
@ -93,6 +94,7 @@ public class Recipient implements RecipientModifiedListener {
|
|||
private boolean profileSharing;
|
||||
private String notificationChannel;
|
||||
|
||||
private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static @NonNull Recipient from(@NonNull Context context, @NonNull Address address, boolean asynchronous) {
|
||||
|
@ -121,51 +123,55 @@ public class Recipient implements RecipientModifiedListener {
|
|||
this.resolving = true;
|
||||
|
||||
if (stale != null) {
|
||||
this.name = stale.name;
|
||||
this.contactUri = stale.contactUri;
|
||||
this.systemContactPhoto = stale.systemContactPhoto;
|
||||
this.groupAvatarId = stale.groupAvatarId;
|
||||
this.color = stale.color;
|
||||
this.customLabel = stale.customLabel;
|
||||
this.messageRingtone = stale.messageRingtone;
|
||||
this.callRingtone = stale.callRingtone;
|
||||
this.mutedUntil = stale.mutedUntil;
|
||||
this.blocked = stale.blocked;
|
||||
this.messageVibrate = stale.messageVibrate;
|
||||
this.callVibrate = stale.callVibrate;
|
||||
this.expireMessages = stale.expireMessages;
|
||||
this.seenInviteReminder = stale.seenInviteReminder;
|
||||
this.defaultSubscriptionId = stale.defaultSubscriptionId;
|
||||
this.registered = stale.registered;
|
||||
this.notificationChannel = stale.notificationChannel;
|
||||
this.profileKey = stale.profileKey;
|
||||
this.profileName = stale.profileName;
|
||||
this.profileAvatar = stale.profileAvatar;
|
||||
this.profileSharing = stale.profileSharing;
|
||||
this.name = stale.name;
|
||||
this.contactUri = stale.contactUri;
|
||||
this.systemContactPhoto = stale.systemContactPhoto;
|
||||
this.groupAvatarId = stale.groupAvatarId;
|
||||
this.color = stale.color;
|
||||
this.customLabel = stale.customLabel;
|
||||
this.messageRingtone = stale.messageRingtone;
|
||||
this.callRingtone = stale.callRingtone;
|
||||
this.mutedUntil = stale.mutedUntil;
|
||||
this.blocked = stale.blocked;
|
||||
this.messageVibrate = stale.messageVibrate;
|
||||
this.callVibrate = stale.callVibrate;
|
||||
this.expireMessages = stale.expireMessages;
|
||||
this.seenInviteReminder = stale.seenInviteReminder;
|
||||
this.defaultSubscriptionId = stale.defaultSubscriptionId;
|
||||
this.registered = stale.registered;
|
||||
this.notificationChannel = stale.notificationChannel;
|
||||
this.profileKey = stale.profileKey;
|
||||
this.profileName = stale.profileName;
|
||||
this.profileAvatar = stale.profileAvatar;
|
||||
this.profileSharing = stale.profileSharing;
|
||||
this.unidentifiedAccessMode = stale.unidentifiedAccessMode;
|
||||
|
||||
this.participants.clear();
|
||||
this.participants.addAll(stale.participants);
|
||||
}
|
||||
|
||||
if (details.isPresent()) {
|
||||
this.name = details.get().name;
|
||||
this.systemContactPhoto = details.get().systemContactPhoto;
|
||||
this.groupAvatarId = details.get().groupAvatarId;
|
||||
this.color = details.get().color;
|
||||
this.messageRingtone = details.get().messageRingtone;
|
||||
this.callRingtone = details.get().callRingtone;
|
||||
this.mutedUntil = details.get().mutedUntil;
|
||||
this.blocked = details.get().blocked;
|
||||
this.messageVibrate = details.get().messageVibrateState;
|
||||
this.callVibrate = details.get().callVibrateState;
|
||||
this.expireMessages = details.get().expireMessages;
|
||||
this.seenInviteReminder = details.get().seenInviteReminder;
|
||||
this.defaultSubscriptionId = details.get().defaultSubscriptionId;
|
||||
this.registered = details.get().registered;
|
||||
this.notificationChannel = details.get().notificationChannel;
|
||||
this.profileKey = details.get().profileKey;
|
||||
this.profileName = details.get().profileName;
|
||||
this.profileAvatar = details.get().profileAvatar;
|
||||
this.profileSharing = details.get().profileSharing;
|
||||
this.name = details.get().name;
|
||||
this.systemContactPhoto = details.get().systemContactPhoto;
|
||||
this.groupAvatarId = details.get().groupAvatarId;
|
||||
this.color = details.get().color;
|
||||
this.messageRingtone = details.get().messageRingtone;
|
||||
this.callRingtone = details.get().callRingtone;
|
||||
this.mutedUntil = details.get().mutedUntil;
|
||||
this.blocked = details.get().blocked;
|
||||
this.messageVibrate = details.get().messageVibrateState;
|
||||
this.callVibrate = details.get().callVibrateState;
|
||||
this.expireMessages = details.get().expireMessages;
|
||||
this.seenInviteReminder = details.get().seenInviteReminder;
|
||||
this.defaultSubscriptionId = details.get().defaultSubscriptionId;
|
||||
this.registered = details.get().registered;
|
||||
this.notificationChannel = details.get().notificationChannel;
|
||||
this.profileKey = details.get().profileKey;
|
||||
this.profileName = details.get().profileName;
|
||||
this.profileAvatar = details.get().profileAvatar;
|
||||
this.profileSharing = details.get().profileSharing;
|
||||
this.unidentifiedAccessMode = details.get().unidentifiedAccessMode;
|
||||
|
||||
this.participants.clear();
|
||||
this.participants.addAll(details.get().participants);
|
||||
}
|
||||
|
@ -175,28 +181,29 @@ public class Recipient implements RecipientModifiedListener {
|
|||
public void onSuccess(RecipientDetails result) {
|
||||
if (result != null) {
|
||||
synchronized (Recipient.this) {
|
||||
Recipient.this.name = result.name;
|
||||
Recipient.this.contactUri = result.contactUri;
|
||||
Recipient.this.systemContactPhoto = result.systemContactPhoto;
|
||||
Recipient.this.groupAvatarId = result.groupAvatarId;
|
||||
Recipient.this.color = result.color;
|
||||
Recipient.this.customLabel = result.customLabel;
|
||||
Recipient.this.messageRingtone = result.messageRingtone;
|
||||
Recipient.this.callRingtone = result.callRingtone;
|
||||
Recipient.this.mutedUntil = result.mutedUntil;
|
||||
Recipient.this.blocked = result.blocked;
|
||||
Recipient.this.messageVibrate = result.messageVibrateState;
|
||||
Recipient.this.callVibrate = result.callVibrateState;
|
||||
Recipient.this.expireMessages = result.expireMessages;
|
||||
Recipient.this.seenInviteReminder = result.seenInviteReminder;
|
||||
Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId;
|
||||
Recipient.this.registered = result.registered;
|
||||
Recipient.this.notificationChannel = result.notificationChannel;
|
||||
Recipient.this.profileKey = result.profileKey;
|
||||
Recipient.this.profileName = result.profileName;
|
||||
Recipient.this.profileAvatar = result.profileAvatar;
|
||||
Recipient.this.profileSharing = result.profileSharing;
|
||||
Recipient.this.profileName = result.profileName;
|
||||
Recipient.this.name = result.name;
|
||||
Recipient.this.contactUri = result.contactUri;
|
||||
Recipient.this.systemContactPhoto = result.systemContactPhoto;
|
||||
Recipient.this.groupAvatarId = result.groupAvatarId;
|
||||
Recipient.this.color = result.color;
|
||||
Recipient.this.customLabel = result.customLabel;
|
||||
Recipient.this.messageRingtone = result.messageRingtone;
|
||||
Recipient.this.callRingtone = result.callRingtone;
|
||||
Recipient.this.mutedUntil = result.mutedUntil;
|
||||
Recipient.this.blocked = result.blocked;
|
||||
Recipient.this.messageVibrate = result.messageVibrateState;
|
||||
Recipient.this.callVibrate = result.callVibrateState;
|
||||
Recipient.this.expireMessages = result.expireMessages;
|
||||
Recipient.this.seenInviteReminder = result.seenInviteReminder;
|
||||
Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId;
|
||||
Recipient.this.registered = result.registered;
|
||||
Recipient.this.notificationChannel = result.notificationChannel;
|
||||
Recipient.this.profileKey = result.profileKey;
|
||||
Recipient.this.profileName = result.profileName;
|
||||
Recipient.this.profileAvatar = result.profileAvatar;
|
||||
Recipient.this.profileSharing = result.profileSharing;
|
||||
Recipient.this.profileName = result.profileName;
|
||||
Recipient.this.unidentifiedAccessMode = result.unidentifiedAccessMode;
|
||||
|
||||
Recipient.this.participants.clear();
|
||||
Recipient.this.participants.addAll(result.participants);
|
||||
|
@ -221,28 +228,30 @@ public class Recipient implements RecipientModifiedListener {
|
|||
}
|
||||
|
||||
Recipient(@NonNull Address address, @NonNull RecipientDetails details) {
|
||||
this.address = address;
|
||||
this.contactUri = details.contactUri;
|
||||
this.name = details.name;
|
||||
this.systemContactPhoto = details.systemContactPhoto;
|
||||
this.groupAvatarId = details.groupAvatarId;
|
||||
this.color = details.color;
|
||||
this.customLabel = details.customLabel;
|
||||
this.messageRingtone = details.messageRingtone;
|
||||
this.callRingtone = details.callRingtone;
|
||||
this.mutedUntil = details.mutedUntil;
|
||||
this.blocked = details.blocked;
|
||||
this.messageVibrate = details.messageVibrateState;
|
||||
this.callVibrate = details.callVibrateState;
|
||||
this.expireMessages = details.expireMessages;
|
||||
this.seenInviteReminder = details.seenInviteReminder;
|
||||
this.defaultSubscriptionId = details.defaultSubscriptionId;
|
||||
this.registered = details.registered;
|
||||
this.notificationChannel = details.notificationChannel;
|
||||
this.profileKey = details.profileKey;
|
||||
this.profileName = details.profileName;
|
||||
this.profileAvatar = details.profileAvatar;
|
||||
this.profileSharing = details.profileSharing;
|
||||
this.address = address;
|
||||
this.contactUri = details.contactUri;
|
||||
this.name = details.name;
|
||||
this.systemContactPhoto = details.systemContactPhoto;
|
||||
this.groupAvatarId = details.groupAvatarId;
|
||||
this.color = details.color;
|
||||
this.customLabel = details.customLabel;
|
||||
this.messageRingtone = details.messageRingtone;
|
||||
this.callRingtone = details.callRingtone;
|
||||
this.mutedUntil = details.mutedUntil;
|
||||
this.blocked = details.blocked;
|
||||
this.messageVibrate = details.messageVibrateState;
|
||||
this.callVibrate = details.callVibrateState;
|
||||
this.expireMessages = details.expireMessages;
|
||||
this.seenInviteReminder = details.seenInviteReminder;
|
||||
this.defaultSubscriptionId = details.defaultSubscriptionId;
|
||||
this.registered = details.registered;
|
||||
this.notificationChannel = details.notificationChannel;
|
||||
this.profileKey = details.profileKey;
|
||||
this.profileName = details.profileName;
|
||||
this.profileAvatar = details.profileAvatar;
|
||||
this.profileSharing = details.profileSharing;
|
||||
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
||||
|
||||
this.participants.addAll(details.participants);
|
||||
this.resolving = false;
|
||||
}
|
||||
|
@ -616,6 +625,18 @@ public class Recipient implements RecipientModifiedListener {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized UnidentifiedAccessMode getUnidentifiedAccessMode() {
|
||||
return unidentifiedAccessMode;
|
||||
}
|
||||
|
||||
public void setUnidentifiedAccessMode(@NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
|
||||
synchronized (this) {
|
||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized boolean isSystemContact() {
|
||||
return contactUri != null;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.SoftHashMap;
|
||||
|
@ -152,56 +153,58 @@ class RecipientProvider {
|
|||
}
|
||||
|
||||
static class RecipientDetails {
|
||||
@Nullable final String name;
|
||||
@Nullable final String customLabel;
|
||||
@Nullable final Uri systemContactPhoto;
|
||||
@Nullable final Uri contactUri;
|
||||
@Nullable final Long groupAvatarId;
|
||||
@Nullable final MaterialColor color;
|
||||
@Nullable final Uri messageRingtone;
|
||||
@Nullable final Uri callRingtone;
|
||||
final long mutedUntil;
|
||||
@Nullable final VibrateState messageVibrateState;
|
||||
@Nullable final VibrateState callVibrateState;
|
||||
final boolean blocked;
|
||||
final int expireMessages;
|
||||
@NonNull final List<Recipient> participants;
|
||||
@Nullable final String profileName;
|
||||
final boolean seenInviteReminder;
|
||||
final Optional<Integer> defaultSubscriptionId;
|
||||
@NonNull final RegisteredState registered;
|
||||
@Nullable final byte[] profileKey;
|
||||
@Nullable final String profileAvatar;
|
||||
final boolean profileSharing;
|
||||
final boolean systemContact;
|
||||
@Nullable final String notificationChannel;
|
||||
@Nullable final String name;
|
||||
@Nullable final String customLabel;
|
||||
@Nullable final Uri systemContactPhoto;
|
||||
@Nullable final Uri contactUri;
|
||||
@Nullable final Long groupAvatarId;
|
||||
@Nullable final MaterialColor color;
|
||||
@Nullable final Uri messageRingtone;
|
||||
@Nullable final Uri callRingtone;
|
||||
final long mutedUntil;
|
||||
@Nullable final VibrateState messageVibrateState;
|
||||
@Nullable final VibrateState callVibrateState;
|
||||
final boolean blocked;
|
||||
final int expireMessages;
|
||||
@NonNull final List<Recipient> participants;
|
||||
@Nullable final String profileName;
|
||||
final boolean seenInviteReminder;
|
||||
final Optional<Integer> defaultSubscriptionId;
|
||||
@NonNull final RegisteredState registered;
|
||||
@Nullable final byte[] profileKey;
|
||||
@Nullable final String profileAvatar;
|
||||
final boolean profileSharing;
|
||||
final boolean systemContact;
|
||||
@Nullable final String notificationChannel;
|
||||
@NonNull final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||
|
||||
RecipientDetails(@Nullable String name, @Nullable Long groupAvatarId,
|
||||
boolean systemContact, @Nullable RecipientSettings settings,
|
||||
@Nullable List<Recipient> participants)
|
||||
{
|
||||
this.groupAvatarId = groupAvatarId;
|
||||
this.systemContactPhoto = settings != null ? Util.uri(settings.getSystemContactPhotoUri()) : null;
|
||||
this.customLabel = settings != null ? settings.getSystemPhoneLabel() : null;
|
||||
this.contactUri = settings != null ? Util.uri(settings.getSystemContactUri()) : null;
|
||||
this.color = settings != null ? settings.getColor() : null;
|
||||
this.messageRingtone = settings != null ? settings.getMessageRingtone() : null;
|
||||
this.callRingtone = settings != null ? settings.getCallRingtone() : null;
|
||||
this.mutedUntil = settings != null ? settings.getMuteUntil() : 0;
|
||||
this.messageVibrateState = settings != null ? settings.getMessageVibrateState() : null;
|
||||
this.callVibrateState = settings != null ? settings.getCallVibrateState() : null;
|
||||
this.blocked = settings != null && settings.isBlocked();
|
||||
this.expireMessages = settings != null ? settings.getExpireMessages() : 0;
|
||||
this.participants = participants == null ? new LinkedList<>() : participants;
|
||||
this.profileName = settings != null ? settings.getProfileName() : null;
|
||||
this.seenInviteReminder = settings != null && settings.hasSeenInviteReminder();
|
||||
this.defaultSubscriptionId = settings != null ? settings.getDefaultSubscriptionId() : Optional.absent();
|
||||
this.registered = settings != null ? settings.getRegistered() : RegisteredState.UNKNOWN;
|
||||
this.profileKey = settings != null ? settings.getProfileKey() : null;
|
||||
this.profileAvatar = settings != null ? settings.getProfileAvatar() : null;
|
||||
this.profileSharing = settings != null && settings.isProfileSharing();
|
||||
this.systemContact = systemContact;
|
||||
this.notificationChannel = settings != null ? settings.getNotificationChannel() : null;
|
||||
this.groupAvatarId = groupAvatarId;
|
||||
this.systemContactPhoto = settings != null ? Util.uri(settings.getSystemContactPhotoUri()) : null;
|
||||
this.customLabel = settings != null ? settings.getSystemPhoneLabel() : null;
|
||||
this.contactUri = settings != null ? Util.uri(settings.getSystemContactUri()) : null;
|
||||
this.color = settings != null ? settings.getColor() : null;
|
||||
this.messageRingtone = settings != null ? settings.getMessageRingtone() : null;
|
||||
this.callRingtone = settings != null ? settings.getCallRingtone() : null;
|
||||
this.mutedUntil = settings != null ? settings.getMuteUntil() : 0;
|
||||
this.messageVibrateState = settings != null ? settings.getMessageVibrateState() : null;
|
||||
this.callVibrateState = settings != null ? settings.getCallVibrateState() : null;
|
||||
this.blocked = settings != null && settings.isBlocked();
|
||||
this.expireMessages = settings != null ? settings.getExpireMessages() : 0;
|
||||
this.participants = participants == null ? new LinkedList<>() : participants;
|
||||
this.profileName = settings != null ? settings.getProfileName() : null;
|
||||
this.seenInviteReminder = settings != null && settings.hasSeenInviteReminder();
|
||||
this.defaultSubscriptionId = settings != null ? settings.getDefaultSubscriptionId() : Optional.absent();
|
||||
this.registered = settings != null ? settings.getRegistered() : RegisteredState.UNKNOWN;
|
||||
this.profileKey = settings != null ? settings.getProfileKey() : null;
|
||||
this.profileAvatar = settings != null ? settings.getProfileAvatar() : null;
|
||||
this.profileSharing = settings != null && settings.isProfileSharing();
|
||||
this.systemContact = systemContact;
|
||||
this.notificationChannel = settings != null ? settings.getNotificationChannel() : null;
|
||||
this.unidentifiedAccessMode = settings != null ? settings.getUnidentifiedAccessMode() : UnidentifiedAccessMode.DISABLED;
|
||||
|
||||
if (name == null && settings != null) this.name = settings.getSystemDisplayName();
|
||||
else this.name = name;
|
||||
|
|
|
@ -40,7 +40,8 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
|||
public static final int FOREGROUND_ID = 313399;
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private static SignalServiceMessagePipe pipe = null;
|
||||
private static SignalServiceMessagePipe pipe = null;
|
||||
public static SignalServiceMessagePipe unidentifiedPipe = null;
|
||||
|
||||
private final Context context;
|
||||
private final NetworkRequirement networkRequirement;
|
||||
|
@ -114,9 +115,10 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
|||
}
|
||||
}
|
||||
|
||||
private void shutdown(SignalServiceMessagePipe pipe) {
|
||||
private void shutdown(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
|
||||
try {
|
||||
pipe.shutdown();
|
||||
unidentifiedPipe.shutdown();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, t);
|
||||
}
|
||||
|
@ -126,6 +128,10 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
|||
return pipe;
|
||||
}
|
||||
|
||||
public static @Nullable SignalServiceMessagePipe getUnidentifiedPipe() {
|
||||
return unidentifiedPipe;
|
||||
}
|
||||
|
||||
private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
MessageRetrievalThread() {
|
||||
|
@ -140,9 +146,11 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
|||
waitForConnectionNecessary();
|
||||
|
||||
Log.i(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
pipe = receiver.createMessagePipe();
|
||||
unidentifiedPipe = receiver.createUnidentifiedMessagePipe();
|
||||
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
SignalServiceMessagePipe unidentifiedLocalPipe = unidentifiedPipe;
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary()) {
|
||||
|
@ -163,7 +171,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
|
|||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(localPipe);
|
||||
shutdown(localPipe, unidentifiedLocalPipe);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Looping...");
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirementProvider;
|
||||
import org.thoughtcrime.securesms.jobmanager.requirements.RequirementListener;
|
||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MessageRetrievalService extends Service implements InjectableType, RequirementListener {
|
||||
|
||||
private static final String TAG = MessageRetrievalService.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_ACTIVITY_STARTED = "ACTIVITY_STARTED";
|
||||
public static final String ACTION_ACTIVITY_FINISHED = "ACTIVITY_FINISHED";
|
||||
public static final String ACTION_PUSH_RECEIVED = "PUSH_RECEIVED";
|
||||
public static final String ACTION_INITIALIZE = "INITIALIZE";
|
||||
public static final int FOREGROUND_ID = 313399;
|
||||
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private NetworkRequirement networkRequirement;
|
||||
private NetworkRequirementProvider networkRequirementProvider;
|
||||
|
||||
@Inject
|
||||
public SignalServiceMessageReceiver receiver;
|
||||
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private MessageRetrievalThread retrievalThread = null;
|
||||
|
||||
public static SignalServiceMessagePipe pipe = null;
|
||||
public static SignalServiceMessagePipe unidentifiedPipe = null;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ApplicationContext.getInstance(this).injectDependencies(this);
|
||||
|
||||
networkRequirement = new NetworkRequirement(this);
|
||||
networkRequirementProvider = new NetworkRequirementProvider(this);
|
||||
|
||||
networkRequirementProvider.setListener(this);
|
||||
|
||||
retrievalThread = new MessageRetrievalThread();
|
||||
retrievalThread.start();
|
||||
|
||||
setForegroundIfNecessary();
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent == null) return START_STICKY;
|
||||
|
||||
if (ACTION_ACTIVITY_STARTED.equals(intent.getAction())) incrementActive();
|
||||
else if (ACTION_ACTIVITY_FINISHED.equals(intent.getAction())) decrementActive();
|
||||
else if (ACTION_PUSH_RECEIVED.equals(intent.getAction())) incrementPushReceived(intent);
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (retrievalThread != null) {
|
||||
retrievalThread.stopThread();
|
||||
}
|
||||
|
||||
sendBroadcast(new Intent("org.thoughtcrime.securesms.RESTART"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequirementStatusChanged() {
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setForegroundIfNecessary() {
|
||||
if (TextSecurePreferences.isGcmDisabled(this)) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NotificationChannels.OTHER);
|
||||
builder.setContentTitle(getString(R.string.MessageRetrievalService_signal));
|
||||
builder.setContentText(getString(R.string.MessageRetrievalService_background_connection_enabled));
|
||||
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
||||
builder.setWhen(0);
|
||||
builder.setSmallIcon(R.drawable.ic_signal_grey_24dp);
|
||||
startForeground(FOREGROUND_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void incrementActive() {
|
||||
activeActivities++;
|
||||
Log.d(TAG, "Active Count: " + activeActivities);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void decrementActive() {
|
||||
activeActivities--;
|
||||
Log.d(TAG, "Active Count: " + activeActivities);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void incrementPushReceived(Intent intent) {
|
||||
pushPending.add(intent);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void decrementPushReceived() {
|
||||
if (!pushPending.isEmpty()) {
|
||||
Intent intent = pushPending.remove(0);
|
||||
GcmBroadcastReceiver.completeWakefulIntent(intent);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean isConnectionNecessary() {
|
||||
boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(this);
|
||||
|
||||
Log.d(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s, gcm disabled: %b",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size(), isGcmDisabled));
|
||||
|
||||
return TextSecurePreferences.isPushRegistered(this) &&
|
||||
TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty() || isGcmDisabled) &&
|
||||
networkRequirement.isPresent();
|
||||
}
|
||||
|
||||
private synchronized void waitForConnectionNecessary() {
|
||||
try {
|
||||
while (!isConnectionNecessary()) wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdown(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
|
||||
try {
|
||||
pipe.shutdown();
|
||||
unidentifiedPipe.shutdown();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerActivityStarted(Context activity) {
|
||||
Intent intent = new Intent(activity, MessageRetrievalService.class);
|
||||
intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_STARTED);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static void registerActivityStopped(Context activity) {
|
||||
Intent intent = new Intent(activity, MessageRetrievalService.class);
|
||||
intent.setAction(MessageRetrievalService.ACTION_ACTIVITY_FINISHED);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public static @Nullable SignalServiceMessagePipe getPipe() {
|
||||
return pipe;
|
||||
}
|
||||
|
||||
public static @Nullable SignalServiceMessagePipe getUnidentifiedPipe() {
|
||||
return unidentifiedPipe;
|
||||
}
|
||||
|
||||
private class MessageRetrievalThread extends Thread implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private AtomicBoolean stopThread = new AtomicBoolean(false);
|
||||
|
||||
MessageRetrievalThread() {
|
||||
super("MessageRetrievalService");
|
||||
setUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!stopThread.get()) {
|
||||
Log.i(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
|
||||
Log.i(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
unidentifiedPipe = receiver.createUnidentifiedMessagePipe();
|
||||
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
SignalServiceMessagePipe unidentifiedLocalPipe = unidentifiedPipe;
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary() && !stopThread.get()) {
|
||||
try {
|
||||
Log.i(TAG, "Reading message...");
|
||||
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
envelope -> {
|
||||
Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
new PushContentReceiveJob(getApplicationContext()).processEnvelope(envelope);
|
||||
decrementPushReceived();
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(localPipe, unidentifiedLocalPipe);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Looping...");
|
||||
}
|
||||
|
||||
Log.i(TAG, "Exiting...");
|
||||
}
|
||||
|
||||
private void stopThread() {
|
||||
stopThread.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.w(TAG, "*** Uncaught exception!");
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RotateSenderCertificateListener extends PersistentAlarmManagerListener {
|
||||
|
||||
private static final long INTERVAL = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
@Override
|
||||
protected long getNextScheduledExecutionTime(Context context) {
|
||||
return TextSecurePreferences.getUnidentifiedAccessCertificateRotationTime(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long onAlarm(Context context, long scheduledTime) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RotateCertificateJob(context));
|
||||
|
||||
long nextTime = System.currentTimeMillis() + INTERVAL;
|
||||
TextSecurePreferences.setUnidentifiedAccessCertificateRotationTime(context, nextTime);
|
||||
|
||||
return nextTime;
|
||||
}
|
||||
|
||||
public static void schedule(Context context) {
|
||||
new RotateSenderCertificateListener().onReceive(context, new Intent());
|
||||
}
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.greenrobot.eventbus.EventBus;
|
|||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.WebRtcCallActivity;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
|
@ -992,7 +993,9 @@ public class WebRtcCallService extends Service implements InjectableType,
|
|||
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()), callMessage);
|
||||
messageSender.sendCallMessage(new SignalServiceAddress(recipient.getAddress().toPhoneString()),
|
||||
UnidentifiedAccessUtil.getAccessFor(WebRtcCallService.this, recipient),
|
||||
callMessage);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
|||
public class IncomingJoinedMessage extends IncomingTextMessage {
|
||||
|
||||
public IncomingJoinedMessage(Address sender) {
|
||||
super(sender, 1, System.currentTimeMillis(), null, Optional.<SignalServiceGroup>absent(), 0);
|
||||
super(sender, 1, System.currentTimeMillis(), null, Optional.<SignalServiceGroup>absent(), 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -42,6 +42,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
private final boolean push;
|
||||
private final int subscriptionId;
|
||||
private final long expiresInMillis;
|
||||
private final boolean unidentified;
|
||||
|
||||
public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) {
|
||||
this.message = message.getDisplayMessageBody();
|
||||
|
@ -56,11 +57,12 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.expiresInMillis = 0;
|
||||
this.groupId = null;
|
||||
this.push = false;
|
||||
this.unidentified = false;
|
||||
}
|
||||
|
||||
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
|
||||
String encodedBody, Optional<SignalServiceGroup> group,
|
||||
long expiresInMillis)
|
||||
long expiresInMillis, boolean unidentified)
|
||||
{
|
||||
this.message = encodedBody;
|
||||
this.sender = sender;
|
||||
|
@ -73,6 +75,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.push = true;
|
||||
this.subscriptionId = -1;
|
||||
this.expiresInMillis = expiresInMillis;
|
||||
this.unidentified = unidentified;
|
||||
|
||||
if (group.isPresent()) {
|
||||
this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get().getGroupId(), false));
|
||||
|
@ -94,6 +97,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.push = (in.readInt() == 1);
|
||||
this.subscriptionId = in.readInt();
|
||||
this.expiresInMillis = in.readLong();
|
||||
this.unidentified = in.readInt() == 1;
|
||||
}
|
||||
|
||||
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
|
||||
|
@ -109,6 +113,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.push = base.isPush();
|
||||
this.subscriptionId = base.getSubscriptionId();
|
||||
this.expiresInMillis = base.getExpiresIn();
|
||||
this.unidentified = base.isUnidentified();
|
||||
}
|
||||
|
||||
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
|
||||
|
@ -130,6 +135,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.push = fragments.get(0).isPush();
|
||||
this.subscriptionId = fragments.get(0).getSubscriptionId();
|
||||
this.expiresInMillis = fragments.get(0).getExpiresIn();
|
||||
this.unidentified = fragments.get(0).isUnidentified();
|
||||
}
|
||||
|
||||
protected IncomingTextMessage(@NonNull Address sender, @Nullable Address groupId)
|
||||
|
@ -146,6 +152,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||
this.push = true;
|
||||
this.subscriptionId = -1;
|
||||
this.expiresInMillis = 0;
|
||||
this.unidentified = false;
|
||||
}
|
||||
|
||||
public int getSubscriptionId() {
|
||||
|
@ -240,6 +247,10 @@ public class IncomingTextMessage implements Parcelable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isUnidentified() {
|
||||
return unidentified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
@ -258,5 +269,6 @@ public class IncomingTextMessage implements Parcelable {
|
|||
out.writeParcelable(groupId, flags);
|
||||
out.writeInt(push ? 1 : 0);
|
||||
out.writeInt(subscriptionId);
|
||||
out.writeInt(unidentified ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.transport;
|
|||
import java.io.IOException;
|
||||
|
||||
public class RetryLaterException extends Exception {
|
||||
public RetryLaterException() {}
|
||||
|
||||
public RetryLaterException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
|
@ -19,6 +18,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase;
|
|||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
|
||||
|
@ -82,7 +82,7 @@ public class IdentityUtil {
|
|||
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId());
|
||||
|
||||
if (remote) {
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
|
||||
|
||||
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
|
||||
else incoming = new IncomingIdentityDefaultMessage(incoming);
|
||||
|
@ -102,7 +102,7 @@ public class IdentityUtil {
|
|||
}
|
||||
|
||||
if (remote) {
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
|
||||
|
||||
if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming);
|
||||
else incoming = new IncomingIdentityDefaultMessage(incoming);
|
||||
|
@ -132,14 +132,14 @@ public class IdentityUtil {
|
|||
while ((groupRecord = reader.getNext()) != null) {
|
||||
if (groupRecord.getMembers().contains(recipient.getAddress()) && groupRecord.isActive()) {
|
||||
SignalServiceGroup group = new SignalServiceGroup(groupRecord.getId());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.of(group), 0, false);
|
||||
IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming);
|
||||
|
||||
smsDatabase.insertMessageInbox(groupUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0);
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(recipient.getAddress(), 1, time, null, Optional.absent(), 0, false);
|
||||
IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming);
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ public class TextSecurePreferences {
|
|||
private static final String UPDATE_APK_DOWNLOAD_ID = "pref_update_apk_download_id";
|
||||
private static final String UPDATE_APK_DIGEST = "pref_update_apk_digest";
|
||||
private static final String SIGNED_PREKEY_ROTATION_TIME_PREF = "pref_signed_pre_key_rotation_time";
|
||||
|
||||
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
|
||||
private static final String SHOW_INVITE_REMINDER_PREF = "pref_show_invite_reminder";
|
||||
public static final String MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size";
|
||||
|
@ -163,6 +164,10 @@ public class TextSecurePreferences {
|
|||
private static final String NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version";
|
||||
|
||||
private static final String NEEDS_MESSAGE_PULL = "pref_needs_message_pull";
|
||||
|
||||
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF = "pref_unidentified_access_certificate_rotation_time";
|
||||
private static final String UNIDENTIFIED_ACCESS_CERTIFICATE = "pref_unidentified_access_certificate";
|
||||
private static final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access";
|
||||
|
||||
public static boolean isScreenLockEnabled(@NonNull Context context) {
|
||||
return getBooleanPreference(context, SCREEN_LOCK, false);
|
||||
|
@ -506,6 +511,39 @@ public class TextSecurePreferences {
|
|||
return getBooleanPreference(context, IN_THREAD_NOTIFICATION_PREF, true);
|
||||
}
|
||||
|
||||
public static long getUnidentifiedAccessCertificateRotationTime(Context context) {
|
||||
return getLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, 0L);
|
||||
}
|
||||
|
||||
public static void setUnidentifiedAccessCertificateRotationTime(Context context, long value) {
|
||||
setLongPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE_ROTATION_TIME_PREF, value);
|
||||
}
|
||||
|
||||
public static void setUnidentifiedAccessCertificate(Context context, byte[] value) {
|
||||
setStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, Base64.encodeBytes(value));
|
||||
}
|
||||
|
||||
public static byte[] getUnidentifiedAccessCertificate(Context context) {
|
||||
try {
|
||||
String result = getStringPreference(context, UNIDENTIFIED_ACCESS_CERTIFICATE, null);
|
||||
if (result != null) {
|
||||
return Base64.decode(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isUniversalUnidentifiedAccess(Context context) {
|
||||
return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false);
|
||||
}
|
||||
|
||||
public static void setUniversalUnidentifiedAccess(Context context, boolean value) {
|
||||
setBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, value);
|
||||
}
|
||||
|
||||
public static long getSignedPreKeyRotationTime(Context context) {
|
||||
return getLongPreference(context, SIGNED_PREKEY_ROTATION_TIME_PREF, 0L);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue