Support for sealed sender - Part 2

This commit is contained in:
Greyson Parrelli 2018-10-11 16:45:22 -07:00
parent 5f31762220
commit 2acab563d9
51 changed files with 819 additions and 394 deletions

View File

@ -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.10.0-RC1'
compile 'org.whispersystems:signal-service-android:2.10.0'
compile 'org.whispersystems:webrtc-android:M69'
compile "me.leolin:ShortcutBadger:1.1.16"
@ -147,6 +147,127 @@ 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',
'com.android.support:multidex:ecf6098572e23b5155bab3b9a82b2fd1530eda6c6c157745e0f5287c66eec60c',
'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:f6f144dc5df938b3a7d58dacf734f3f673e402b241a51b429de6b19ea3ef0393',
'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.signal:signal-metadata-android:d9d798aab7ee7200373ecff8718baf8aaeb632c123604e8a41b7b4c0c97eeee1',
'org.whispersystems:signal-service-java:092b0c23d914a5c6c04188fd06097e3351ad251d631e95cd0119ac60c85969d8',
'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:signal-protocol-android:b41b7c3a47b0a4ce35802474ae7c5ddc8f033d80209e98abf7ec669543b2b3ee',
'org.signal:signal-metadata-java:af1d0dd766b1e301ed1c44e65161084cf03e2587fe97fdd29ecbea58c6aa6930',
'org.whispersystems:signal-protocol-java:b08207f7e1847228f2a1f0d49e113f93c96c6ed8490be14edddd4be55b2a4a4e',
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
'com.googlecode.libphonenumber:libphonenumber:183392c0565be16d3f6f86680b4106bbde6fe31a402ad21bf9823d938c0c8706',
'com.fasterxml.jackson.core:jackson-databind:0fb4e079c118e752cc94c15ad22e6782b0dfc5dc09145f4813fb39d82e686047',
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
'com.madgag.spongycastle:pkix:0d9cca6991f68eb373cfad309d5268c9fc38db5efb5fe00dcccf5c973af1eca1',
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
'org.threeten:threetenbp:f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7',
'org.whispersystems:curve25519-android:82595394422b957d4a5b5f1b27b75ba25cf6dc4db4d312418ca38cd6fff279ca',
'com.fasterxml.jackson.core:jackson-annotations:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a',
'com.fasterxml.jackson.core:jackson-core:a2bebaa325ad25455b02149c67e6052367a7d7fc1ce77de000eed284a5214eac',
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
'org.whispersystems:curve25519-java:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
]
}
android {
flavorDimensions "none"
compileSdkVersion 28
@ -169,7 +290,7 @@ android {
project.ext.set("archivesBaseName", "Signal");
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.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\""
@ -178,7 +299,7 @@ android {
buildConfigField "String", "USER_AGENT", "\"OWA\""
buildConfigField "boolean", "DEV_BUILD", "false"
buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86"

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

View File

@ -20,17 +20,39 @@
android:paddingLeft="10dp"
android:paddingRight="10dp">
<TextView android:id="@+id/error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:textSize="16sp"
android:padding="5dp"
tools:visibility="visible"
android:text="@string/message_details_header__issues_need_your_attention"
android:drawableLeft="@drawable/ic_info_outline_light"
android:drawableStart="@drawable/ic_info_outline_light"
android:gravity="center_vertical" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/error_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
android:textSize="16sp"
android:padding="5dp"
tools:visibility="visible"
android:text="@string/message_details_header__issues_need_your_attention"
android:drawableLeft="@drawable/ic_info_outline_light"
android:drawableStart="@drawable/ic_info_outline_light"
android:gravity="center_vertical" />
<Button
android:id="@+id/resend_button"
android:layout_width="wrap_content"
android:layout_height="38sp"
style="@style/InfoButton"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_gravity="center_vertical"
android:drawableLeft="@drawable/ic_refresh_white_18dp"
android:text="@string/message_recipients_list_item__resend"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<TableLayout android:id="@+id/metadata_container"
android:layout_width="match_parent"

View File

@ -80,19 +80,13 @@
android:visibility="gone"
tools:visibility="gone" />
<Button android:id="@+id/resend_button"
android:layout_width="wrap_content"
android:layout_height="38sp"
style="@style/InfoButton"
android:paddingLeft="10dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_gravity="center_vertical"
android:drawableLeft="@drawable/ic_refresh_white_18dp"
android:text="@string/message_recipients_list_item__resend"
android:visibility="gone"
tools:visibility="gone" />
<ImageView android:id="@+id/ud_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:src="@drawable/ic_unidentified_delivery"
android:tint="?attr/conversation_item_sent_text_secondary_color"/>
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_unidentified_delivery"
android:tint="?conversation_item_sent_text_secondary_color"/>
<TextView
android:id="@+id/ud_learn_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preferences_communication__sealed_sender_learn_more"
android:textColor="@color/signal_primary"/>
</LinearLayout>

View File

@ -86,6 +86,9 @@
<string name="ClearProfileActivity_remove">Remove</string>
<string name="ClearProfileActivity_remove_profile_photo">Remove profile photo?</string>
<!-- CommunicationActions -->
<string name="CommunicationActions_no_browser_found">No web browser found.</string>
<!-- ConfirmIdentityDialog -->
<string name="ConfirmIdentityDialog_your_safety_number_with_s_has_changed">Your safety number with %1$s has changed. This could either mean that someone is trying to intercept your communication, or that %2$s simply reinstalled Signal.</string>
<string name="ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact">You may wish to verify your safety number with this contact.</string>
@ -1204,6 +1207,12 @@
<string name="preferences_chats__message_text_size">Message font size</string>
<string name="preferences_events__contact_joined_signal">Contact joined Signal</string>
<string name="preferences_notifications__priority">Priority</string>
<string name="preferences_communication__category_sealed_sender">Sealed Sender</string>
<string name="preferences_communication__sealed_sender_display_indicators">Display indicators</string>
<string name="preferences_communication__sealed_sender_display_indicators_description">Show a status icon when you select "Message details" on messages that were delivered using sealed sender.</string>
<string name="preferences_communication__sealed_sender_allow_from_anyone">Allow from anyone</string>
<string name="preferences_communication__sealed_sender_allow_from_anyone_description">Enable sealed sender for incoming messages from non-contacts and people with whom you have not shared your profile.</string>
<string name="preferences_communication__sealed_sender_learn_more">Learn more</string>
<!-- **************************************** -->
<!-- menus -->

View File

@ -69,6 +69,27 @@
<PreferenceCategory android:layout="@layout/preference_divider"/>
<PreferenceCategory android:title="@string/preferences_communication__category_sealed_sender">
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_show_unidentifed_delivery_indicators"
android:title="@string/preferences_communication__sealed_sender_display_indicators"
android:summary="@string/preferences_communication__sealed_sender_display_indicators_description"/>
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="false"
android:key="pref_universal_unidentified_access"
android:title="@string/preferences_communication__sealed_sender_allow_from_anyone"
android:summary="@string/preferences_communication__sealed_sender_allow_from_anyone_description"/>
<Preference
android:key="pref_unidentified_learn_more"
android:layout="@layout/unidentified_delivery_learn_more" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_divider"/>
<PreferenceCategory android:title="@string/preferences_app_protection__registration_lock">
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="false"

View File

@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.logging.AndroidLogger;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log;
@ -109,6 +110,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializeCircumvention();
initializeWebRtc();
initializePendingMessages();
initializeUnidentifiedDeliveryAbilityRefresh();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@ -281,4 +283,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
TextSecurePreferences.setNeedsMessagePull(this, false);
}
}
private void initializeUnidentifiedDeliveryAbilityRefresh() {
if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) {
jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob(this));
}
}
}

View File

@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -95,6 +94,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
public static final int IMAGE_CACHE_CLEANUP = 406;
public static final int WORKMANAGER_MIGRATION = 408;
public static final int COLOR_MIGRATION = 412;
public static final int UNIDENTIFIED_DELIVERY = 422;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -120,6 +120,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(IMAGE_CACHE_CLEANUP);
add(WORKMANAGER_MIGRATION);
add(COLOR_MIGRATION);
add(UNIDENTIFIED_DELIVERY);
}};
private MasterSecret masterSecret;
@ -358,6 +359,18 @@ public class DatabaseUpgradeActivity extends BaseActivity {
Log.i(TAG, "Color migration took " + (System.currentTimeMillis() - startTime) + " ms");
}
if (params[0] < UNIDENTIFIED_DELIVERY) {
if (TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "MultiDevice: Disabling UD (will be re-enabled if possible after pending refresh).");
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
}
Log.i(TAG, "Scheduling UD attributes refresh.");
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RefreshAttributesJob(context));
}
return null;
}

View File

@ -11,6 +11,7 @@ import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.transition.TransitionInflater;
import org.thoughtcrime.securesms.logging.Log;
import android.view.MenuItem;
import android.view.View;
@ -179,6 +180,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
Optional<byte[]> profileKey = Optional.of(ProfileKeyUtil.getProfileKey(getContext()));
TextSecurePreferences.setMultiDevice(DeviceActivity.this, true);
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, profileKey, verificationCode);
return SUCCESS;

View File

@ -9,6 +9,8 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.logging.Log;
import android.view.LayoutInflater;
import android.view.View;
@ -172,6 +174,10 @@ public class DeviceListFragment extends ListFragment
protected Void doInBackground(Void... params) {
try {
accountManager.removeDevice(deviceId);
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new RefreshUnidentifiedDeliveryAbilityJob(getContext()));
} catch (IOException e) {
Log.w(TAG, e);
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();

View File

@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -87,6 +88,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private View metadataContainer;
private View expiresContainer;
private TextView errorText;
private View resendButton;
private TextView sentDate;
private TextView receivedDate;
private TextView expiresInText;
@ -176,6 +178,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
recipientsList = findViewById(R.id.recipients_list);
metadataContainer = header.findViewById(R.id.metadata_container);
errorText = header.findViewById(R.id.error_text);
resendButton = header.findViewById(R.id.resend_button);
sentDate = header.findViewById(R.id.sent_time);
receivedContainer = header.findViewById(R.id.received_container);
receivedDate = header.findViewById(R.id.received_time);
@ -362,7 +365,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
List<RecipientDeliveryStatus> recipients = new LinkedList<>();
if (!messageRecord.getRecipient().isGroupRecipient()) {
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), -1));
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1));
} else {
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
@ -370,12 +373,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
for (Recipient recipient : group) {
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, -1));
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
}
} else {
for (GroupReceiptInfo info : receiptInfoList) {
recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
getStatusFor(info.getStatus(), messageRecord.isPending()),
getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
info.isUnidentified(),
info.getTimestamp()));
}
}
@ -392,16 +396,28 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
}
inflateMessageViewIfAbsent(messageRecord);
updateRecipients(messageRecord, messageRecord.getRecipient(), recipients);
if (messageRecord.isFailed()) {
boolean isGroupNetworkFailure = messageRecord.isFailed() && !messageRecord.getNetworkFailures().isEmpty();
boolean isIndividualNetworkFailure = messageRecord.isFailed() && !isPushGroup && messageRecord.getIdentityKeyMismatches().isEmpty();
if (isGroupNetworkFailure || isIndividualNetworkFailure) {
errorText.setVisibility(View.VISIBLE);
resendButton.setVisibility(View.VISIBLE);
resendButton.setOnClickListener(this::onResendClicked);
metadataContainer.setVisibility(View.GONE);
} else if (messageRecord.isFailed()) {
errorText.setVisibility(View.VISIBLE);
resendButton.setVisibility(View.GONE);
resendButton.setOnClickListener(null);
metadataContainer.setVisibility(View.GONE);
} else {
updateTransport(messageRecord);
updateTime(messageRecord);
updateExpirationTime(messageRecord);
errorText.setVisibility(View.GONE);
resendButton.setVisibility(View.GONE);
resendButton.setOnClickListener(null);
metadataContainer.setVisibility(View.VISIBLE);
}
}
@ -413,14 +429,19 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
else return RecipientDeliveryStatus.Status.PENDING;
}
private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending) {
private RecipientDeliveryStatus.Status getStatusFor(int groupStatus, boolean pending, boolean failed) {
if (groupStatus == GroupReceiptDatabase.STATUS_READ) return RecipientDeliveryStatus.Status.READ;
else if (groupStatus == GroupReceiptDatabase.STATUS_DELIVERED) return RecipientDeliveryStatus.Status.DELIVERED;
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && failed) return RecipientDeliveryStatus.Status.UNKNOWN;
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED && !pending) return RecipientDeliveryStatus.Status.SENT;
else if (groupStatus == GroupReceiptDatabase.STATUS_UNDELIVERED) return RecipientDeliveryStatus.Status.PENDING;
else if (groupStatus == GroupReceiptDatabase.STATUS_UNKNOWN) return RecipientDeliveryStatus.Status.UNKNOWN;
throw new AssertionError();
}
private void onResendClicked(View v) {
MessageSender.resend(MessageDetailsActivity.this, messageRecord);
resendButton.setVisibility(View.GONE);
}
}
}

View File

@ -81,11 +81,13 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
private final Recipient recipient;
private final Status deliveryStatus;
private final boolean isUnidentified;
private final long timestamp;
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, long timestamp) {
RecipientDeliveryStatus(Recipient recipient, Status deliveryStatus, boolean isUnidentified, long timestamp) {
this.recipient = recipient;
this.deliveryStatus = deliveryStatus;
this.isUnidentified = isUnidentified;
this.timestamp = timestamp;
}
@ -93,6 +95,10 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
return deliveryStatus;
}
boolean isUnidentified() {
return isUnidentified;
}
public long getTimestamp() {
return timestamp;
}

View File

@ -16,13 +16,12 @@
*/
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@ -30,15 +29,13 @@ import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDelive
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
/**
@ -58,8 +55,8 @@ public class MessageRecipientListItem extends RelativeLayout
private TextView errorDescription;
private TextView actionDescription;
private Button conflictButton;
private Button resendButton;
private AvatarImageView contactPhotoImage;
private ImageView unidentifiedDeliveryIcon;
private DeliveryStatusView deliveryStatusView;
public MessageRecipientListItem(Context context) {
@ -73,13 +70,13 @@ public class MessageRecipientListItem extends RelativeLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
this.fromView = findViewById(R.id.from);
this.errorDescription = findViewById(R.id.error_description);
this.actionDescription = findViewById(R.id.action_description);
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
this.conflictButton = findViewById(R.id.conflict_button);
this.resendButton = findViewById(R.id.resend_button);
this.deliveryStatusView = findViewById(R.id.delivery_status);
this.fromView = findViewById(R.id.from);
this.errorDescription = findViewById(R.id.error_description);
this.actionDescription = findViewById(R.id.action_description);
this.contactPhotoImage = findViewById(R.id.contact_photo_image);
this.conflictButton = findViewById(R.id.conflict_button);
this.unidentifiedDeliveryIcon = findViewById(R.id.ud_indicator);
this.deliveryStatusView = findViewById(R.id.delivery_status);
}
public void set(final GlideRequests glideRequests,
@ -94,6 +91,7 @@ public class MessageRecipientListItem extends RelativeLayout
fromView.setText(member.getRecipient());
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
setIssueIndicators(record, isPushGroup);
unidentifiedDeliveryIcon.setVisibility(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()) && member.isUnidentified() ? VISIBLE : GONE);
}
private void setIssueIndicators(final MessageRecord record,
@ -105,25 +103,13 @@ public class MessageRecipientListItem extends RelativeLayout
String errorText = "";
if (keyMismatch != null) {
resendButton.setVisibility(View.GONE);
conflictButton.setVisibility(View.VISIBLE);
errorText = getContext().getString(R.string.MessageDetailsRecipient_new_safety_number);
conflictButton.setOnClickListener(v -> new ConfirmIdentityDialog(getContext(), record, keyMismatch).show());
} else if (networkFailure != null || (!isPushGroup && record.isFailed())) {
resendButton.setVisibility(View.VISIBLE);
resendButton.setEnabled(true);
resendButton.requestFocus();
} else if ((networkFailure != null && !record.isPending()) || (!isPushGroup && record.isFailed())) {
conflictButton.setVisibility(View.GONE);
errorText = getContext().getString(R.string.MessageDetailsRecipient_failed_to_send);
resendButton.setOnClickListener(v -> {
resendButton.setVisibility(View.GONE);
errorDescription.setVisibility(View.GONE);
actionDescription.setVisibility(View.VISIBLE);
actionDescription.setText(R.string.message_recipients_list_item__resending);
new ResendAsyncTask(record, networkFailure).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
} else {
if (record.isOutgoing()) {
if (member.getDeliveryStatus() == RecipientDeliveryStatus.Status.PENDING || member.getDeliveryStatus() == RecipientDeliveryStatus.Status.UNKNOWN) {
@ -142,7 +128,6 @@ public class MessageRecipientListItem extends RelativeLayout
deliveryStatusView.setVisibility(View.GONE);
}
resendButton.setVisibility(View.GONE);
conflictButton.setVisibility(View.GONE);
}
@ -183,31 +168,4 @@ public class MessageRecipientListItem extends RelativeLayout
contactPhotoImage.setAvatar(glideRequests, recipient, false);
});
}
@SuppressLint("StaticFieldLeak")
private class ResendAsyncTask extends AsyncTask<Void,Void,Void> {
private final Context context;
private final MessageRecord record;
private final NetworkFailure failure;
ResendAsyncTask(MessageRecord record, NetworkFailure failure) {
this.context = getContext().getApplicationContext();
this.record = record;
this.failure = failure;
}
@Override
protected Void doInBackground(Void... params) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
mmsDatabase.removeFailure(record.getId(), failure);
if (record.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(context, record, failure.getAddress());
} else {
MessageSender.resend(context, record);
}
return null;
}
}
}

View File

@ -7,9 +7,8 @@ import android.support.v7.widget.SwitchCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadReceiptUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
@ -39,7 +38,9 @@ public class ReadReceiptsIntroFragment extends Fragment {
TextSecurePreferences.setReadReceiptsEnabled(getContext(), isChecked);
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), isChecked));
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
isChecked,
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
});
return v;

View File

@ -30,6 +30,7 @@ import android.telephony.PhoneNumberUtils;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
import android.view.MenuItem;
@ -740,6 +741,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
}
if (blocked && (recipient.resolve().isSystemContact() || recipient.resolve().isProfileSharing())) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RotateProfileKeyJob(context));
}
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceBlockedUpdateJob(context));

View File

@ -31,4 +31,8 @@ public class ProfileKeyUtil {
}
}
public static synchronized @NonNull byte[] rotateProfileKey(@NonNull Context context) {
TextSecurePreferences.setProfileKey(context, null);
return getProfileKey(context);
}
}

View File

@ -10,6 +10,7 @@ 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.database.RecipientDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -40,6 +41,11 @@ public class UnidentifiedAccessUtil {
public static Optional<UnidentifiedAccessPair> getAccessFor(@NonNull Context context,
@NonNull Recipient recipient)
{
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Log.i(TAG, "Unidentified delivery is disabled. [other]");
return Optional.absent();
}
try {
byte[] theirUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
@ -49,9 +55,9 @@ public class UnidentifiedAccessUtil {
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));
Log.i(TAG, "Their access key present? " + (theirUnidentifiedAccessKey != null));
Log.i(TAG, "Our access key present? " + (ourUnidentifiedAccessKey != null));
Log.i(TAG, "Our certificate present? " + (ourUnidentifiedAccessCertificate != null));
if (theirUnidentifiedAccessKey != null &&
ourUnidentifiedAccessKey != null &&
@ -71,6 +77,11 @@ public class UnidentifiedAccessUtil {
}
public static Optional<UnidentifiedAccessPair> getAccessForSync(@NonNull Context context) {
if (!TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Log.i(TAG, "Unidentified delivery is disabled. [self]");
return Optional.absent();
}
try {
byte[] ourUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(context);
byte[] ourUnidentifiedAccessCertificate = TextSecurePreferences.getUnidentifiedAccessCertificate(context);

View File

@ -34,6 +34,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
public abstract void markExpireStarted(long messageId, long startTime);
public abstract void markAsSent(long messageId, boolean secure);
public abstract void markUnidentified(long messageId, boolean unidentified);
public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) {
List<IdentityKeyMismatch> items = new ArrayList<IdentityKeyMismatch>() {{

View File

@ -343,11 +343,11 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId);
}
// public void markAsSending(long messageId) {
// long threadId = getThreadIdForMessage(messageId);
// updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId));
// notifyConversationListeners(threadId);
// }
public void markAsSending(long messageId) {
long threadId = getThreadIdForMessage(messageId);
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE, Optional.of(threadId));
notifyConversationListeners(threadId);
}
public void markAsSentFailed(long messageId) {
long threadId = getThreadIdForMessage(messageId);
@ -403,6 +403,7 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId);
}
@Override
public void markUnidentified(long messageId, boolean unidentified) {
ContentValues contentValues = new ContentValues();
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);

View File

@ -270,8 +270,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_BODY,
MmsDatabase.QUOTE_MISSING,
MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS,
MmsDatabase.UNIDENTIFIED};
MmsDatabase.SHARED_CONTACTS};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();

View File

@ -239,10 +239,15 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_TYPE | (isSecure ? Types.PUSH_MESSAGE_BIT | Types.SECURE_MESSAGE_BIT : 0));
}
public void markAsSending(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
}
public void markAsMissedCall(long id) {
updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE);
}
@Override
public void markUnidentified(long id, boolean unidentified) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
@ -695,7 +700,9 @@ public class SmsDatabase extends MessagingDatabase {
public Cursor getMessageCursor(long messageId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
return db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null);
Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null);
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId));
return cursor;
}
public boolean deleteMessage(long messageId) {

View File

@ -289,6 +289,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
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.execSQL("ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0");
}
db.setTransactionSuccessful();

View File

@ -3,6 +3,9 @@ package org.thoughtcrime.securesms.dependencies;
import android.content.Context;
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.logging.Log;
import org.greenrobot.eventbus.EventBus;
@ -87,7 +90,10 @@ import dagger.Provides;
AppProtectionPreferenceFragment.class,
GcmBroadcastReceiver.class,
RotateCertificateJob.class,
SendDeliveryReceiptJob.class})
SendDeliveryReceiptJob.class,
RotateProfileKeyJob.class,
MultiDeviceConfigurationUpdateJob.class,
RefreshUnidentifiedDeliveryAbilityJob.class})
public class SignalCommunicationModule {
private static final String TAG = SignalCommunicationModule.class.getSimpleName();

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
@ -13,6 +14,8 @@ import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
@ -61,6 +64,11 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
public void onRun(MasterSecret masterSecret)
throws IOException, UntrustedIdentityException
{
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
}
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {

View File

@ -0,0 +1,87 @@
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;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class MultiDeviceConfigurationUpdateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceConfigurationUpdateJob.class.getSimpleName();
private static final String KEY_READ_RECEIPTS_ENABLED = "read_receipts_enabled";
private static final String KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED = "unidentified_delivery_indicators_enabled";
@Inject transient SignalServiceMessageSender messageSender;
private boolean readReceiptsEnabled;
private boolean unidentifiedDeliveryIndicatorsEnabled;
public MultiDeviceConfigurationUpdateJob() {
super(null, null);
}
public MultiDeviceConfigurationUpdateJob(Context context, boolean readReceiptsEnabled, boolean unidentifiedDeliveryIndicatorsEnabled) {
super(context, JobParameters.newBuilder()
.withGroupId("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__")
.withNetworkRequirement()
.create());
this.readReceiptsEnabled = readReceiptsEnabled;
this.unidentifiedDeliveryIndicatorsEnabled = unidentifiedDeliveryIndicatorsEnabled;
}
@Override
protected void initialize(@NonNull SafeData data) {
readReceiptsEnabled = data.getBoolean(KEY_READ_RECEIPTS_ENABLED);
unidentifiedDeliveryIndicatorsEnabled = data.getBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED);
}
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.putBoolean(KEY_READ_RECEIPTS_ENABLED, readReceiptsEnabled)
.putBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, unidentifiedDeliveryIndicatorsEnabled)
.build();
}
@Override
public void onRun() throws IOException, UntrustedIdentityException {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
}
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled), Optional.of(unidentifiedDeliveryIndicatorsEnabled))),
UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override
public boolean onShouldRetry(Exception e) {
return e instanceof PushNetworkException;
}
@Override
public void onCanceled() {
Log.w(TAG, "**** Failed to synchronize read receipts state!");
}
}

View File

@ -116,7 +116,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
throws IOException, UntrustedIdentityException, NetworkException
{
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.w(TAG, "Not multi device, aborting...");
Log.i(TAG, "Not multi device, aborting...");
return;
}

View File

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -67,6 +68,11 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
@Override
public void onRun(MasterSecret masterSecret) throws Exception {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
}
File contactDataFile = createTempFile("multidevice-contact-update");
GroupDatabase.Reader reader = null;

View File

@ -62,7 +62,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
@Override
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
if (!TextSecurePreferences.isMultiDevice(getContext())) {
Log.w(TAG, "Not multi device...");
Log.i(TAG, "Not multi device...");
return;
}

View File

@ -9,6 +9,7 @@ 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.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -22,6 +23,10 @@ import javax.inject.Inject;
import androidx.work.Data;
/**
* Use {@link MultiDeviceConfigurationUpdateJob}.
*/
@Deprecated
public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 1L;
@ -59,7 +64,12 @@ public class MultiDeviceReadReceiptUpdateJob extends ContextJob implements Injec
@Override
public void onRun() throws IOException, UntrustedIdentityException {
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled))),
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
}
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(enabled), Optional.absent())),
UnidentifiedAccessUtil.getAccessForSync(context));
}

View File

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
@ -91,7 +92,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
@Override
public void onRun(MasterSecret masterSecret) throws IOException, UntrustedIdentityException {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.w(TAG, "Not multi device...");
Log.i(TAG, "Not multi device...");
return;
}
@ -101,8 +102,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
}
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages),
UnidentifiedAccessUtil.getAccessForSync(context));
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override

View File

@ -91,7 +91,7 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
public void onRun() throws IOException, UntrustedIdentityException {
try {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.w(TAG, "Not multi device...");
Log.i(TAG, "Not multi device...");
return;
}

View File

@ -20,6 +20,7 @@ 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.signal.libsignal.metadata.SelfSendException;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
@ -36,6 +37,7 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
@ -242,6 +244,8 @@ public class PushDecryptJob extends ContextJob {
handleNeedsDeliveryReceipt(content, message);
}
} else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true);
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
@ -273,7 +277,11 @@ public class PushDecryptJob extends ContextJob {
} catch (ProtocolInvalidVersionException e) {
Log.w(TAG, e);
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolInvalidMessageException | ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
} catch (ProtocolInvalidMessageException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
}
catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (StorageFailedException e) {
@ -290,6 +298,8 @@ public class PushDecryptJob extends ContextJob {
handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) {
Log.w(TAG, e);
} catch (SelfSendException e) {
Log.i(TAG, "Dropping UD message from self.");
}
}
@ -542,6 +552,10 @@ public class PushDecryptJob extends ContextJob {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceContactUpdateJob(getContext(), true));
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RefreshUnidentifiedDeliveryAbilityJob(context));
}
if (message.isGroupsRequest()) {
@ -559,7 +573,9 @@ public class PushDecryptJob extends ContextJob {
if (message.isConfigurationRequest()) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), TextSecurePreferences.isReadReceiptsEnabled(getContext())));
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
TextSecurePreferences.isReadReceiptsEnabled(getContext()),
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
}
}
@ -670,7 +686,17 @@ public class PushDecryptJob extends ContextJob {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null);
if (recipients.getAddress().isGroup()) {
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipients.getAddress().toGroupString(), false);
for (Recipient member : members) {
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
}
}
database.markAsSent(messageId, true);
database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize()));
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId)) {
ApplicationContext.getInstance(context)
@ -753,11 +779,19 @@ public class PushDecryptJob extends ContextJob {
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null);
database = DatabaseFactory.getMmsDatabase(context);
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false);
for (Recipient member : members) {
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
}
} else {
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database = DatabaseFactory.getSmsDatabase(context);
database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize()));
}
database.markAsSent(messageId, true);

View File

@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@ -99,6 +100,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
.build();
}
@Override
protected void onAdded() {
DatabaseFactory.getMmsDatabase(context).markAsSending(messageId);
}
@Override
public void onPushSend()
throws IOException, MmsException, NoSuchMessageException, RetryLaterException
@ -110,6 +116,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
try {
Log.i(TAG, "Sending message: " + messageId);
List<Address> target;
if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress));

View File

@ -10,16 +10,19 @@ 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.database.RecipientDatabase.UnidentifiedAccessMode;
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;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -68,6 +71,11 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
return dataBuilder.putLong(KEY_MESSAGE_ID, messageId).build();
}
@Override
protected void onAdded() {
DatabaseFactory.getMmsDatabase(context).markAsSending(messageId);
}
@Override
public void onPushSend()
throws RetryLaterException, MmsException, NoSuchMessageException,
@ -85,6 +93,19 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
markAttachmentsUploaded(messageId, message.getAttachments());
database.markUnidentified(messageId, unidentified);
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Recipient recipient = message.getRecipient().resolve();
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
if (unidentified && (accessMode == UnidentifiedAccessMode.UNKNOWN || accessMode == UnidentifiedAccessMode.DISABLED)) {
Log.i(TAG, "Marking recipient as UD-enabled following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
Log.i(TAG, "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
}
}
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
database.markExpireStarted(messageId);
expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn());

View File

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
@ -19,11 +20,11 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
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;
@ -68,6 +69,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
@Override
public void onAdded() {
Log.i(TAG, "onAdded() messageId: " + messageId);
DatabaseFactory.getSmsDatabase(context).markAsSending(messageId);
}
@Override
@ -83,6 +85,19 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
database.markAsSent(messageId, true);
database.markUnidentified(messageId, unidentified);
if (TextSecurePreferences.isUnidentifiedDeliveryEnabled(context)) {
Recipient recipient = record.getRecipient().resolve();
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
if (unidentified && (accessMode == UnidentifiedAccessMode.UNKNOWN || accessMode == UnidentifiedAccessMode.DISABLED)) {
Log.i(TAG, "Marking recipient as UD-enabled following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED);
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
Log.i(TAG, "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
}
}
if (record.getExpiresIn() > 0) {
database.markExpireStarted(messageId);
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());

View File

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
@ -10,10 +11,6 @@ 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;
@ -63,6 +60,10 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
signalAccountManager.setAccountAttributes(signalingKey, registrationId, fetchesMessages, pin,
unidentifiedAccessKey, universalUnidentifiedAccess);
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RefreshUnidentifiedDeliveryAbilityJob(context));
}
@Override

View File

@ -0,0 +1,96 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
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.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
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.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class RefreshUnidentifiedDeliveryAbilityJob extends ContextJob implements InjectableType {
private static final String TAG = RefreshUnidentifiedDeliveryAbilityJob.class.getSimpleName();
@Inject transient SignalServiceMessageReceiver receiver;
public RefreshUnidentifiedDeliveryAbilityJob() {
super(null, null);
}
public RefreshUnidentifiedDeliveryAbilityJob(Context context) {
super(context, new JobParameters.Builder()
.withNetworkRequirement()
.create());
}
@Override
protected void initialize(@NonNull SafeData data) { }
@Override
protected @NonNull Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
public void onRun() throws Exception {
byte[] profileKey = ProfileKeyUtil.getProfileKey(context);
SignalServiceProfile profile = retrieveProfile(TextSecurePreferences.getLocalNumber(context));
boolean enabled = profile.getUnidentifiedAccess() != null && isValidVerifier(profileKey, profile.getUnidentifiedAccess());
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, enabled);
Log.i(TAG, "Set UD status to: " + enabled);
}
@Override
protected void onCanceled() {
}
@Override
protected boolean onShouldRetry(Exception exception) {
return exception instanceof PushNetworkException;
}
private SignalServiceProfile retrieveProfile(@NonNull String number) throws IOException {
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
if (pipe != null) {
try {
return pipe.getProfile(new SignalServiceAddress(number), Optional.absent());
} catch (IOException e) {
Log.w(TAG, e);
}
}
return receiver.retrieveProfile(new SignalServiceAddress(number), Optional.absent());
}
private boolean isValidVerifier(@NonNull byte[] profileKey, @NonNull String verifier) {
ProfileCipher profileCipher = new ProfileCipher(profileKey);
try {
return profileCipher.verifyUnidentifiedAccess(Base64.decode(verifier));
} catch (IOException e) {
Log.w(TAG, e);
return false;
}
}
}

View File

@ -30,7 +30,7 @@ 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.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
@ -101,9 +101,8 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
try {
profile = retrieveProfile(number, unidentifiedAccess);
} catch (AuthorizationFailedException e) {
} catch (NonSuccessfulResponseCodeException e) {
if (unidentifiedAccess.isPresent()) {
// XXX Update UI
profile = retrieveProfile(number, Optional.absent());
} else {
throw e;
@ -129,7 +128,10 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
private SignalServiceProfile retrieveProfile(@NonNull String number, Optional<UnidentifiedAccess> unidentifiedAccess)
throws IOException
{
SignalServiceMessagePipe pipe = IncomingMessageObserver.getPipe();
SignalServiceMessagePipe authPipe = IncomingMessageObserver.getPipe();
SignalServiceMessagePipe unidentifiedPipe = IncomingMessageObserver.getUnidentifiedPipe();
SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe
: authPipe;
if (pipe != null) {
try {
@ -169,10 +171,11 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
byte[] profileKey = recipient.getProfileKey();
// XXX Update UI
if (unrestrictedUnidentifiedAccess) {
Log.i(TAG, "Marking recipient UD status as unrestricted.");
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED);
} else if (profileKey == null || unidentifiedAccessVerifier == null) {
Log.i(TAG, "Marking recipient UD status as disabled.");
recipientDatabase.setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED);
} else {
ProfileCipher profileCipher = new ProfileCipher(profileKey);
@ -185,7 +188,9 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
verifiedUnidentifiedAccess = false;
}
recipientDatabase.setUnidentifiedAccessMode(recipient, verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED);
UnidentifiedAccessMode mode = verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED;
Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification.");
recipientDatabase.setUnidentifiedAccessMode(recipient, mode);
}
}

View File

@ -0,0 +1,88 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
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.profiles.AvatarHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.StreamDetails;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.inject.Inject;
import androidx.work.Data;
public class RotateProfileKeyJob extends ContextJob implements InjectableType {
@Inject SignalServiceAccountManager accountManager;
public RotateProfileKeyJob() {
super(null, null);
}
public RotateProfileKeyJob(Context context) {
super(context, new JobParameters.Builder()
.withGroupId("__ROTATE_PROFILE_KEY__")
.withDuplicatesIgnored(true)
.withNetworkRequirement()
.create());
}
@NonNull
@Override
protected Data serialize(@NonNull Data.Builder dataBuilder) {
return dataBuilder.build();
}
@Override
protected void initialize(@NonNull SafeData data) {
}
@Override
public void onRun() throws Exception {
byte[] profileKey = ProfileKeyUtil.rotateProfileKey(context);
accountManager.setProfileName(profileKey, TextSecurePreferences.getProfileName(context));
accountManager.setProfileAvatar(profileKey, getProfileAvatar());
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RefreshAttributesJob(context));
}
@Override
protected void onCanceled() {
}
@Override
protected boolean onShouldRetry(Exception exception) {
return exception instanceof PushNetworkException;
}
private @Nullable StreamDetails getProfileAvatar() {
try {
Address localAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(context));
File avatarFile = AvatarHelper.getAvatarFile(context, localAddress);
if (avatarFile.exists()) {
return new StreamDetails(new FileInputStream(avatarFile), "image/jpeg", avatarFile.length());
}
} catch (IOException e) {
return null;
}
return null;
}
}

View File

@ -10,6 +10,7 @@ import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.Preference;
import android.view.View;
import android.widget.Toast;
import org.thoughtcrime.securesms.ApplicationContext;
@ -20,9 +21,12 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadReceiptUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
@ -34,7 +38,8 @@ import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment implements InjectableType {
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
private static final String PREFERENCE_UNIDENTIFIED_LEARN_MORE = "pref_unidentified_learn_more";
private CheckBoxPreference disablePassphrase;
@ -61,6 +66,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF).setOnPreferenceClickListener(new PassphraseIntervalClickListener());
this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener());
this.findPreference(PREFERENCE_CATEGORY_BLOCKED).setOnPreferenceClickListener(new BlockedContactsClickListener());
this.findPreference(TextSecurePreferences.SHOW_UNIDENTIFIED_DELIVERY_INDICATORS).setOnPreferenceChangeListener(new ShowUnidentifiedDeliveryIndicatorsChangedListener());
this.findPreference(TextSecurePreferences.UNIVERSAL_UNIDENTIFIED_ACCESS).setOnPreferenceChangeListener(new UniversalUnidentifiedAccessChangedListener());
this.findPreference(PREFERENCE_UNIDENTIFIED_LEARN_MORE).setOnPreferenceClickListener(new UnidentifiedLearnMoreClickListener());
disablePassphrase.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
initializeVisibility();
@ -177,7 +185,9 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
boolean enabled = (boolean)newValue;
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new MultiDeviceReadReceiptUpdateJob(getContext(), enabled));
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
enabled,
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext())));
return true;
}
@ -271,4 +281,35 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
}
}
private class ShowUnidentifiedDeliveryIndicatorsChangedListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean enabled = (boolean) newValue;
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new MultiDeviceConfigurationUpdateJob(getContext(),
TextSecurePreferences.isReadReceiptsEnabled(getContext()),
enabled));
return true;
}
}
private class UniversalUnidentifiedAccessChangedListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new RefreshAttributesJob(getContext()));
return true;
}
}
private class UnidentifiedLearnMoreClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
CommunicationActions.openBrowserLink(preference.getContext(), "https://signal.org/blog/secret-sender/");
return true;
}
}
}

View File

@ -24,12 +24,4 @@ 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);
}
}

View File

@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;

View File

@ -41,7 +41,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
private static final long REQUEST_TIMEOUT_MINUTES = 1;
private static SignalServiceMessagePipe pipe = null;
public static SignalServiceMessagePipe unidentifiedPipe = null;
private static SignalServiceMessagePipe unidentifiedPipe = null;
private final Context context;
private final NetworkRequirement networkRequirement;
@ -158,7 +158,7 @@ public class IncomingMessageObserver implements InjectableType, RequirementListe
Log.i(TAG, "Reading message...");
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
envelope -> {
Log.i(TAG, "Retrieved envelope! " + envelope.getSource());
Log.i(TAG, "Retrieved envelope! " + String.valueOf(envelope.getSource()));
new PushContentReceiveJob(context).processEnvelope(envelope);
});
} catch (TimeoutException e) {

View File

@ -1,254 +0,0 @@
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);
}
}
}

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.util;
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@ -10,6 +11,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.TaskStackBuilder;
import android.text.TextUtils;
import android.widget.Toast;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.R;
@ -87,4 +89,13 @@ public class CommunicationActions {
}
context.startActivity(intent);
}
public static void openBrowserLink(@NonNull Context context, @NonNull String link) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.CommunicationActions_no_browser_found, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -164,10 +164,12 @@ 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 final String UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access";
public static final String SHOW_UNIDENTIFIED_DELIVERY_INDICATORS = "pref_show_unidentifed_delivery_indicators";
private static final String UNIDENTIFIED_DELIVERY_ENABLED = "pref_unidentified_delivery_enabled";
public static boolean isScreenLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, SCREEN_LOCK, false);
@ -540,8 +542,16 @@ public class TextSecurePreferences {
return getBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, false);
}
public static void setUniversalUnidentifiedAccess(Context context, boolean value) {
setBooleanPreference(context, UNIVERSAL_UNIDENTIFIED_ACCESS, value);
public static boolean isShowUnidentifiedDeliveryIndicatorsEnabled(Context context) {
return getBooleanPreference(context, SHOW_UNIDENTIFIED_DELIVERY_INDICATORS, false);
}
public static void setIsUnidentifiedDeliveryEnabled(Context context, boolean enabled) {
setBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, enabled);
}
public static boolean isUnidentifiedDeliveryEnabled(Context context) {
return getBooleanPreference(context, UNIDENTIFIED_DELIVERY_ENABLED, true);
}
public static long getSignedPreKeyRotationTime(Context context) {