Remove call log permissions, use SMS Retriever API during registration.

This is to adhere to the Play Store policy updates.

See: https://play.google.com/about/privacy-security-deception/permissions/
This commit is contained in:
Greyson Parrelli 2018-12-12 13:12:44 -08:00
parent 19d5ba5c0e
commit 3b67382f67
7 changed files with 168 additions and 122 deletions

View File

@ -36,7 +36,6 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
@ -45,7 +44,6 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_CALL_STATE"/> <uses-permission android:name="android.permission.READ_CALL_STATE"/>
<!-- For sending/receiving events --> <!-- For sending/receiving events -->

View File

@ -75,14 +75,15 @@ dependencies {
compile 'android.arch.lifecycle:common-java8:1.1.1' compile 'android.arch.lifecycle:common-java8:1.1.1'
compile 'android.arch.work:work-runtime:1.0.0-alpha12' compile 'android.arch.work:work-runtime:1.0.0-alpha12'
compile 'com.google.android.gms:play-services-gcm:9.6.1' compile 'com.google.android.gms:play-services-gcm:16.0.0'
compile 'com.google.android.gms:play-services-maps:9.6.1' compile 'com.google.android.gms:play-services-maps:16.0.0'
compile 'com.google.android.gms:play-services-places:9.6.1' compile 'com.google.android.gms:play-services-places:16.0.0'
compile 'com.google.android.gms:play-services-auth:16.0.1'
compile 'com.google.android.exoplayer:exoplayer-core:2.9.1' compile 'com.google.android.exoplayer:exoplayer-core:2.9.1'
compile 'com.google.android.exoplayer:exoplayer-ui:2.9.1' compile 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
compile 'org.whispersystems:signal-service-android:2.12.3' compile 'org.whispersystems:signal-service-android:2.12.4'
compile 'org.whispersystems:webrtc-android:M69' compile 'org.whispersystems:webrtc-android:M69'
compile "me.leolin:ShortcutBadger:1.1.16" compile "me.leolin:ShortcutBadger:1.1.16"
@ -172,12 +173,13 @@ dependencyVerification {
'android.arch.work:work-runtime:b78d5a7b71dccf6c2549168ca1890f6848ba2d72782b903555b18099c459e472', 'android.arch.work:work-runtime:b78d5a7b71dccf6c2549168ca1890f6848ba2d72782b903555b18099c459e472',
'android.arch.lifecycle:extensions:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6', 'android.arch.lifecycle:extensions:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6',
'android.arch.lifecycle:common-java8:7078b5c8ccb94203df9cc2a463c69cf0021596e6cf966d78fbfd697aaafe0630', 'android.arch.lifecycle:common-java8:7078b5c8ccb94203df9cc2a463c69cf0021596e6cf966d78fbfd697aaafe0630',
'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae', 'com.google.android.gms:play-services-gcm:921b4651a2f4d108753f8b4c5d3844b5f8c1f3c31b740c80f69765082713d004',
'com.google.android.gms:play-services-places:abf3a4a3b146ec7e6e753be62775e512868cf37d6f88ffe2d81167b33b57132b', 'com.google.android.gms:play-services-places:2d5c4e4ac3ee5be21b4ec544411bc51d11457b5ae2fa2a5d4539019f87c233c6',
'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e', 'com.google.android.gms:play-services-maps:07f59c5955b759ce7b80ceaeb8261643c5b79acc9f180df2b7c3987658eed2e8',
'com.google.android.gms:play-services-auth:aec9e1c584d442cb9f59481a50b2c66dc191872607c04d97ecb82dd0eb5149ec',
'com.google.android.exoplayer:exoplayer-ui:7a942afcc402ff01e9bf48e8d3942850986710f06562d50a1408aaf04a683151', 'com.google.android.exoplayer:exoplayer-ui:7a942afcc402ff01e9bf48e8d3942850986710f06562d50a1408aaf04a683151',
'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0', 'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0',
'org.whispersystems:signal-service-android:b02896703fb826792056ad872b812107ec2e80ea18dbeb43b97327d82faa3947', 'org.whispersystems:signal-service-android:ea1b065c4c7d4f14d26a5e954aff6ecdfaf5eb6f02bbb750db1041ed0f22fa32',
'org.whispersystems:webrtc-android:5493c92141ce884fc5ce8240d783232f4fe14bd17a8d0d7d1bd4944d0bd1682f', 'org.whispersystems:webrtc-android:5493c92141ce884fc5ce8240d783232f4fe14bd17a8d0d7d1bd4944d0bd1682f',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774', 'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb', 'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
@ -201,10 +203,14 @@ dependencyVerification {
'com.github.dmytrodanylyk.circular-progress-button:library:8dc6a29a5a8db7b2ad5a9a7fda1dc9ae0893f4c8f0545732b2c63854ea693e8e', 'com.github.dmytrodanylyk.circular-progress-button:library:8dc6a29a5a8db7b2ad5a9a7fda1dc9ae0893f4c8f0545732b2c63854ea693e8e',
'org.signal:android-database-sqlcipher:33d4063336893af00b9d68b418e7b290cace74c20ce8aacffddc0911010d3d73', 'org.signal:android-database-sqlcipher:33d4063336893af00b9d68b418e7b290cace74c20ce8aacffddc0911010d3d73',
'com.googlecode.ez-vcard:ez-vcard:7e24ad50b222d2f70ac91bdccfa3c0f6200b078d797cb784837f75e77bb4210f', 'com.googlecode.ez-vcard:ez-vcard:7e24ad50b222d2f70ac91bdccfa3c0f6200b078d797cb784837f75e77bb4210f',
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70', 'com.google.android.gms:play-services-iid:62aa9d8fbfb66ab68d5afdfe78db3d5da1f6adcc19abac142cee25fb23d3391f',
'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49', 'com.google.android.gms:play-services-auth-api-phone:19365818b9ceb048ef48db12b5ffadd5eb86dbeb2c7c7b823bfdd89c665f42e5',
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086', 'com.google.android.gms:play-services-auth-base:51dc02ad2f8d1d9dff7b5b52c4df2c6c12ef7df55d752e919d5cb4dd6002ecd0',
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d', 'com.google.android.gms:play-services-base:aca10c780c3219bc50f3db06734f4ab88badd3113c564c0a3156ff8ff674655b',
'com.google.android.gms:play-services-stats:5b2d8281adbfd6e74d2295c94bab9ea80fc9a84dfbb397995673f5af4d4c6368',
'com.google.android.gms:play-services-places-placereport:04f8baeb1f8f8a734c7d4b1701a3974281b45591affa7e963b59dd019b8abc6e',
'com.google.android.gms:play-services-tasks:b31c18d8d1cc8d9814f295ee7435471333f370ba5bd904ca14f8f2bec4f35c35',
'com.google.android.gms:play-services-basement:e08bfd1e87c4e50ef76161d7ac76b873aeb975367eeb3afa4abe62ea1887c7c6',
'com.android.support:support-v4:8b9031381c678d628c9e47b566ae1d161e1c9710f7855c759beeac7596cecf30', 'com.android.support:support-v4:8b9031381c678d628c9e47b566ae1d161e1c9710f7855c759beeac7596cecf30',
'com.android.support:support-fragment:3772fc738ada86824ba1a4b3f197c3dbd67b7ddcfe2c9db1de95ef2e3487a915', 'com.android.support:support-fragment:3772fc738ada86824ba1a4b3f197c3dbd67b7ddcfe2c9db1de95ef2e3487a915',
'com.android.support:animated-vector-drawable:271ecbc906cda8dcd9e655ba0473129c3408a4189c806f616c378e6fd18fb3b7', 'com.android.support:animated-vector-drawable:271ecbc906cda8dcd9e655ba0473129c3408a4189c806f616c378e6fd18fb3b7',
@ -244,7 +250,7 @@ dependencyVerification {
'com.android.support:support-annotations:5d5b9414f02d3fa0ee7526b8d5ddae0da67c8ecc8c4d63ffa6cf91488a93b927', 'com.android.support:support-annotations:5d5b9414f02d3fa0ee7526b8d5ddae0da67c8ecc8c4d63ffa6cf91488a93b927',
'com.google.guava:listenablefuture:e4ad7607e5c0477c6f890ef26a49cb8d1bb4dffb650bab4502afee64644e3069', 'com.google.guava:listenablefuture:e4ad7607e5c0477c6f890ef26a49cb8d1bb4dffb650bab4502afee64644e3069',
'org.signal:signal-metadata-android:d9d798aab7ee7200373ecff8718baf8aaeb632c123604e8a41b7b4c0c97eeee1', 'org.signal:signal-metadata-android:d9d798aab7ee7200373ecff8718baf8aaeb632c123604e8a41b7b4c0c97eeee1',
'org.whispersystems:signal-service-java:81fcda4be6e0dfc1f3e990b8bf5d2731a2bb6798b67748981e2a0ceba33d5e7a', 'org.whispersystems:signal-service-java:988049d77ee565ec32428c18569777bae7fa89909b0c2969ad87c577ec6999df',
'com.github.bumptech.glide:disklrucache:c1b1b6f5bbd01e2fcdc9d7f60913c8d338bdb65ed4a93bfa02b56f19daaade4b', 'com.github.bumptech.glide:disklrucache:c1b1b6f5bbd01e2fcdc9d7f60913c8d338bdb65ed4a93bfa02b56f19daaade4b',
'com.github.bumptech.glide:annotations:bede99ef9f71517a4274bac18fd3e483e9f2b6108d7d6fe8f4949be4aa4d9512', 'com.github.bumptech.glide:annotations:bede99ef9f71517a4274bac18fd3e483e9f2b6108d7d6fe8f4949be4aa4d9512',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a', 'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',

View File

@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog; import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;

View File

@ -39,9 +39,14 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.dd.CircularProgressButton; import com.dd.CircularProgressButton;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.gcm.GoogleCloudMessaging; import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.tasks.Task;
import com.google.i18n.phonenumbers.AsYouTypeFormatter; import com.google.i18n.phonenumbers.AsYouTypeFormatter;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber; import com.google.i18n.phonenumbers.Phonenumber;
@ -76,6 +81,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory; import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.VerificationCodeParser;
import org.thoughtcrime.securesms.util.BackupUtil; import org.thoughtcrime.securesms.util.BackupUtil;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.Dialogs;
@ -96,6 +102,8 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.LockedException;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -113,8 +121,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
private static final int SCENE_TRANSITION_DURATION = 250; private static final int SCENE_TRANSITION_DURATION = 250;
private static final int DEBUG_TAP_TARGET = 8; private static final int DEBUG_TAP_TARGET = 8;
private static final int DEBUG_TAP_ANNOUNCE = 4; private static final int DEBUG_TAP_ANNOUNCE = 4;
public static final String CHALLENGE_EVENT = "org.thoughtcrime.securesms.CHALLENGE_EVENT";
public static final String CHALLENGE_EXTRA = "CAAChallenge";
public static final String RE_REGISTRATION_EXTRA = "re_registration"; public static final String RE_REGISTRATION_EXTRA = "re_registration";
private static final String TAG = RegistrationActivity.class.getSimpleName(); private static final String TAG = RegistrationActivity.class.getSimpleName();
@ -125,7 +131,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
private TextView countryCode; private TextView countryCode;
private TextView number; private TextView number;
private CircularProgressButton createButton; private CircularProgressButton createButton;
private TextView termsLinkView;
private TextView informationView; private TextView informationView;
private TextView informationToggleText; private TextView informationToggleText;
private TextView title; private TextView title;
@ -150,7 +155,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
private VerificationPinKeyboard keyboard; private VerificationPinKeyboard keyboard;
private VerificationCodeView verificationCodeView; private VerificationCodeView verificationCodeView;
private RegistrationState registrationState; private RegistrationState registrationState;
private ChallengeReceiver challengeReceiver; private SmsRetrieverReceiver smsRetrieverReceiver;
private SignalServiceAccountManager accountManager; private SignalServiceAccountManager accountManager;
private int debugTapCounter; private int debugTapCounter;
@ -313,8 +318,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
Permissions.with(RegistrationActivity.this) Permissions.with(RegistrationActivity.this)
.request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS, .request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_CALL_LOG, Manifest.permission.READ_PHONE_STATE)
Manifest.permission.PROCESS_OUTGOING_CALLS)
.ifNecessary() .ifNecessary()
.withRationaleDialog(getString(R.string.RegistrationActivity_signal_needs_access_to_your_contacts_and_media_in_order_to_connect_with_friends), .withRationaleDialog(getString(R.string.RegistrationActivity_signal_needs_access_to_your_contacts_and_media_in_order_to_connect_with_friends),
R.drawable.ic_contacts_white_48dp, R.drawable.ic_folder_white_48dp) R.drawable.ic_contacts_white_48dp, R.drawable.ic_folder_white_48dp)
@ -448,31 +452,12 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
return; return;
} }
Permissions.with(this)
.request(Manifest.permission.READ_SMS)
.ifNecessary()
.withRationaleDialog(getString(R.string.RegistrationActivity_to_easily_verify_your_phone_number_signal_can_automatically_detect_your_verification_code), R.drawable.ic_textsms_white_48dp)
.onAnyResult(this::handleRegisterWithPermissions)
.execute();
}
private void handleRegisterWithPermissions() {
if (TextUtils.isEmpty(countryCode.getText())) {
Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_country_code), Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(number.getText())) {
Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number), Toast.LENGTH_LONG).show();
return;
}
final String e164number = getConfiguredE164Number(); final String e164number = getConfiguredE164Number();
if (!PhoneNumberFormatter.isValidNumber(e164number)) { if (!PhoneNumberFormatter.isValidNumber(e164number)) {
Dialogs.showAlertDialog(this, getString(R.string.RegistrationActivity_invalid_number), Dialogs.showAlertDialog(this,
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), getString(R.string.RegistrationActivity_invalid_number),
e164number)); String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number));
return; return;
} }
@ -490,11 +475,30 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
} }
} }
@SuppressLint("StaticFieldLeak")
private void handleRequestVerification(@NonNull String e164number, boolean gcmSupported) { private void handleRequestVerification(@NonNull String e164number, boolean gcmSupported) {
createButton.setIndeterminateProgressMode(true); createButton.setIndeterminateProgressMode(true);
createButton.setProgress(50); createButton.setProgress(50);
if (gcmSupported) {
SmsRetrieverClient client = SmsRetriever.getClient(this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(none -> {
Log.i(TAG, "Successfully registered SMS listener.");
requestVerificationCode(e164number, true, true);
});
task.addOnFailureListener(e -> {
Log.w(TAG, "Failed to register SMS listener.", e);
requestVerificationCode(e164number, true, false);
});
} else {
requestVerificationCode(e164number, false, false);
}
}
@SuppressLint("StaticFieldLeak")
private void requestVerificationCode(@NonNull String e164number, boolean gcmSupported, boolean smsRetrieverSupported) {
new AsyncTask<Void, Void, Pair<String, Optional<String>>> () { new AsyncTask<Void, Void, Pair<String, Optional<String>>> () {
@Override @Override
protected @Nullable Pair<String, Optional<String>> doInBackground(Void... voids) { protected @Nullable Pair<String, Optional<String>> doInBackground(Void... voids) {
@ -512,7 +516,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
} }
accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password); accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password);
accountManager.requestSmsVerificationCode(); accountManager.requestSmsVerificationCode(smsRetrieverSupported);
return new Pair<>(password, gcmToken); return new Pair<>(password, gcmToken);
} catch (IOException e) { } catch (IOException e) {
@ -535,20 +539,32 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void handleChallengeReceived(@Nullable String challenge) { private void handleVerificationCodeReceived(@Nullable String code) {
if (challenge != null && challenge.length() == 6 && registrationState.state == RegistrationState.State.VERIFYING) { List<Integer> parsedCode = convertVerificationCodeToDigits(code);
verificationCodeView.clear();
for (int i = 0; i < parsedCode.size(); i++) {
int index = i;
verificationCodeView.postDelayed(() -> verificationCodeView.append(parsedCode.get(index)), i * 200);
}
}
private List<Integer> convertVerificationCodeToDigits(@Nullable String code) {
if (code == null || code.length() != 6 || registrationState.state != RegistrationState.State.VERIFYING) {
return Collections.emptyList();
}
List<Integer> result = new LinkedList<>();
try { try {
for (int i=0;i<challenge.length();i++) { for (int i = 0; i < code.length(); i++) {
final int index = i; result.add(Integer.parseInt(Character.toString(code.charAt(i))));
verificationCodeView.postDelayed(() -> verificationCodeView.append(Integer.parseInt(Character.toString(challenge.charAt(index)))), i * 200);
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w(TAG, e); Log.w(TAG, "Failed to convert code into digits.",e );
verificationCodeView.clear(); return Collections.emptyList();
}
} }
return result;
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@ -1005,15 +1021,15 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
} }
private void initializeChallengeListener() { private void initializeChallengeListener() {
challengeReceiver = new ChallengeReceiver(); smsRetrieverReceiver = new SmsRetrieverReceiver();
IntentFilter filter = new IntentFilter(CHALLENGE_EVENT); IntentFilter filter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
registerReceiver(challengeReceiver, filter); registerReceiver(smsRetrieverReceiver, filter);
} }
private void shutdownChallengeListener() { private void shutdownChallengeListener() {
if (challengeReceiver != null) { if (smsRetrieverReceiver != null) {
unregisterReceiver(challengeReceiver); unregisterReceiver(smsRetrieverReceiver);
challengeReceiver = null; smsRetrieverReceiver = null;
} }
} }
@ -1040,11 +1056,32 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount())); else restoreBackupProgress.setText(getString(R.string.RegistrationActivity_d_messages_so_far, event.getCount()));
} }
private class ChallengeReceiver extends BroadcastReceiver { private class SmsRetrieverReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Got a challenge broadcast..."); Log.i(TAG, "SmsRetrieverReceiver received a broadcast...");
handleChallengeReceived(intent.getStringExtra(CHALLENGE_EXTRA));
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch (status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
Optional<String> code = VerificationCodeParser.parse(context, (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE));
if (code.isPresent()) {
Log.i(TAG, "Received verification code.");
handleVerificationCodeReceived(code.get());
} else {
Log.w(TAG, "Could not parse verification code.");
}
break;
case CommonStatusCodes.TIMEOUT:
Log.w(TAG, "Hit a timeout waiting for the SMS to arrive.");
break;
}
} else {
Log.w(TAG, "SmsRetrieverReceiver received the wrong action?");
}
} }
} }

View File

@ -22,28 +22,19 @@ import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Telephony; import android.provider.Telephony;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.RegistrationActivity;
import org.thoughtcrime.securesms.jobs.SmsReceiveJob; import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SmsListener extends BroadcastReceiver { public class SmsListener extends BroadcastReceiver {
private static final String SMS_RECEIVED_ACTION = Telephony.Sms.Intents.SMS_RECEIVED_ACTION; private static final String SMS_RECEIVED_ACTION = Telephony.Sms.Intents.SMS_RECEIVED_ACTION;
private static final String SMS_DELIVERED_ACTION = Telephony.Sms.Intents.SMS_DELIVER_ACTION; private static final String SMS_DELIVERED_ACTION = Telephony.Sms.Intents.SMS_DELIVER_ACTION;
private static final Pattern CHALLENGE_PATTERN = Pattern.compile(".*Your (Signal|TextSecure) verification code:? ([0-9]{3,4})-([0-9]{3,4}).*", Pattern.DOTALL);
private boolean isExemption(SmsMessage message, String messageBody) { private boolean isExemption(SmsMessage message, String messageBody) {
// ignore CLASS0 ("flash") messages // ignore CLASS0 ("flash") messages
@ -98,9 +89,6 @@ public class SmsListener extends BroadcastReceiver {
if (!ApplicationMigrationService.isDatabaseImported(context)) if (!ApplicationMigrationService.isDatabaseImported(context))
return false; return false;
if (isChallenge(context, messageBody))
return false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
SMS_RECEIVED_ACTION.equals(intent.getAction()) && SMS_RECEIVED_ACTION.equals(intent.getAction()) &&
Util.isDefaultSmsProvider(context)) Util.isDefaultSmsProvider(context))
@ -117,42 +105,11 @@ public class SmsListener extends BroadcastReceiver {
return false; return false;
} }
@VisibleForTesting boolean isChallenge(@NonNull Context context, @Nullable String messageBody) {
if (messageBody == null)
return false;
if (CHALLENGE_PATTERN.matcher(messageBody).matches() &&
TextSecurePreferences.isVerifying(context))
{
return true;
}
return false;
}
@VisibleForTesting String parseChallenge(String messageBody) {
Matcher challengeMatcher = CHALLENGE_PATTERN.matcher(messageBody);
if (!challengeMatcher.matches()) {
throw new AssertionError("Expression should match.");
}
return challengeMatcher.group(2) + challengeMatcher.group(3);
}
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.i("SMSListener", "Got SMS broadcast..."); Log.i("SMSListener", "Got SMS broadcast...");
String messageBody = getSmsMessageBodyFromIntent(intent); if ((intent.getAction().equals(SMS_DELIVERED_ACTION)) ||
if (SMS_RECEIVED_ACTION.equals(intent.getAction()) && isChallenge(context, messageBody)) {
Log.w("SmsListener", "Got challenge!");
Intent challengeIntent = new Intent(RegistrationActivity.CHALLENGE_EVENT);
challengeIntent.putExtra(RegistrationActivity.CHALLENGE_EXTRA, parseChallenge(messageBody));
context.sendBroadcast(challengeIntent);
abortBroadcast();
} else if ((intent.getAction().equals(SMS_DELIVERED_ACTION)) ||
(intent.getAction().equals(SMS_RECEIVED_ACTION)) && isRelevant(context, intent)) (intent.getAction().equals(SMS_RECEIVED_ACTION)) && isRelevant(context, intent))
{ {
Log.i("SmsListener", "Constructing SmsReceiveJob..."); Log.i("SmsListener", "Constructing SmsReceiveJob...");

View File

@ -0,0 +1,44 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.service;
import android.content.Context;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VerificationCodeParser {
private static final Pattern CHALLENGE_PATTERN = Pattern.compile(".*Your (Signal|TextSecure) verification code:? ([0-9]{3,4})-([0-9]{3,4}).*", Pattern.DOTALL);
public static Optional<String> parse(Context context, String messageBody) {
if (messageBody == null) {
return Optional.absent();
}
Matcher challengeMatcher = CHALLENGE_PATTERN.matcher(messageBody);
if (!challengeMatcher.matches() || !TextSecurePreferences.isVerifying(context)) {
return Optional.absent();
}
return Optional.of(challengeMatcher.group(2) + challengeMatcher.group(3));
}
}

View File

@ -1,21 +1,21 @@
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import junit.framework.AssertionFailedError;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.thoughtcrime.securesms.BaseUnitTest; import org.thoughtcrime.securesms.BaseUnitTest;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.contains; import static org.mockito.Matchers.contains;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
public class SmsListenerTest extends BaseUnitTest { public class VerificationCodeParserTest extends BaseUnitTest {
private static Map<String, String> CHALLENGES = new HashMap<String,String>() {{ private static Map<String, String> CHALLENGES = new HashMap<String,String>() {{
put("Your TextSecure verification code: 337-337", "337337"); put("Your TextSecure verification code: 337-337", "337337");
put("XXX\nYour TextSecure verification code: 1337-1337", "13371337"); put("XXX\nYour TextSecure verification code: 1337-1337", "13371337");
@ -34,25 +34,28 @@ public class SmsListenerTest extends BaseUnitTest {
put("XXXYour Signal verification code: 1337-1337", "13371337"); put("XXXYour Signal verification code: 1337-1337", "13371337");
put("Your Signal verification code: 1337-1337XXX", "13371337"); put("Your Signal verification code: 1337-1337XXX", "13371337");
put("Your Signal verification code 1337-1337", "13371337"); put("Your Signal verification code 1337-1337", "13371337");
}};
private SmsListener listener; put("<#>Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337");
put("<#> Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337");
put("<#>Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337");
put("<#> Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337");
put("<#> Your Signal verification code: 1337-1337\n\naAbBcCdDeEf", "13371337");
}};
@Before @Before
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
listener = new SmsListener();
when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true); when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true);
} }
@Test @Test
public void testChallenges() throws Exception { public void testChallenges() {
for (Entry<String,String> challenge : CHALLENGES.entrySet()) { for (Entry<String,String> challenge : CHALLENGES.entrySet()) {
if (!listener.isChallenge(context, challenge.getKey())) { Optional<String> result = VerificationCodeParser.parse(context, challenge.getKey());
throw new AssertionFailedError("SmsListener didn't recognize body as a challenge.");
} assertTrue(result.isPresent());
assertEquals(listener.parseChallenge(challenge.getKey()), challenge.getValue()); assertEquals(result.get(), challenge.getValue());
} }
} }
} }