mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Initial client support for GCM message send/receive
This commit is contained in:
parent
2f39283da3
commit
303d1acd45
@ -34,7 +34,7 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
|
||||
<permission android:name="org.thoughtcrime.securesms.permissionC2D_MESSAGE"
|
||||
<permission android:name="org.thoughtcrime.securesms.permission.C2D_MESSAGE"
|
||||
android:protectionLevel="signature" />
|
||||
<uses-permission android:name="org.thoughtcrime.securesms.permission.C2D_MESSAGE" />
|
||||
|
||||
|
@ -456,6 +456,48 @@
|
||||
android:textSize="16.0sp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/retrieve_directory_complete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:src="@drawable/check"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/retrieve_directory_progress"
|
||||
style="?android:attr/android:progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:visibility="invisible" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/retrieve_directory_text"
|
||||
style="@style/Registration.Constant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="4.0dip"
|
||||
android:paddingRight="8.0dip"
|
||||
android:text="Retrieving directory..."
|
||||
android:textSize="16.0sp" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<TextView
|
||||
|
@ -26,8 +26,8 @@ import android.widget.Button;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.protocol.Tag;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
|
@ -46,15 +46,18 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
private ProgressBar connectingProgress;
|
||||
private ProgressBar verificationProgress;
|
||||
private ProgressBar gcmRegistrationProgress;
|
||||
private ProgressBar retrieveDirectoryProgress;
|
||||
|
||||
private ImageView connectingCheck;
|
||||
private ImageView verificationCheck;
|
||||
private ImageView gcmRegistrationCheck;
|
||||
private ImageView retrieveDirectoryCheck;
|
||||
|
||||
private TextView connectingText;
|
||||
private TextView verificationText;
|
||||
private TextView registrationTimerText;
|
||||
private TextView gcmRegistrationText;
|
||||
private TextView retrieveDirectoryText;
|
||||
|
||||
private Button editButton;
|
||||
private Button verificationFailureButton;
|
||||
@ -108,18 +111,22 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
this.connectingProgress = (ProgressBar) findViewById(R.id.connecting_progress);
|
||||
this.verificationProgress = (ProgressBar) findViewById(R.id.verification_progress);
|
||||
this.gcmRegistrationProgress = (ProgressBar) findViewById(R.id.gcm_registering_progress);
|
||||
this.retrieveDirectoryProgress = (ProgressBar) findViewById(R.id.retrieve_directory_progress);
|
||||
this.connectingCheck = (ImageView) findViewById(R.id.connecting_complete);
|
||||
this.verificationCheck = (ImageView) findViewById(R.id.verification_complete);
|
||||
this.gcmRegistrationCheck = (ImageView) findViewById(R.id.gcm_registering_complete);
|
||||
this.retrieveDirectoryCheck = (ImageView) findViewById(R.id.retrieve_directory_complete);
|
||||
this.connectingText = (TextView) findViewById(R.id.connecting_text);
|
||||
this.verificationText = (TextView) findViewById(R.id.verification_text);
|
||||
this.registrationTimerText = (TextView) findViewById(R.id.registration_timer);
|
||||
this.gcmRegistrationText = (TextView) findViewById(R.id.gcm_registering_text);
|
||||
this.retrieveDirectoryText = (TextView) findViewById(R.id.retrieve_directory_text);
|
||||
this.editButton = (Button) findViewById(R.id.edit_button);
|
||||
this.verificationFailureButton = (Button) findViewById(R.id.verification_failure_edit_button);
|
||||
this.connectivityFailureButton = (Button) findViewById(R.id.connectivity_failure_edit_button);
|
||||
this.timeoutProgressLayout = (RelativeLayout) findViewById(R.id.timer_progress_layout);
|
||||
|
||||
|
||||
this.editButton.setOnClickListener(new EditButtonListener());
|
||||
this.verificationFailureButton.setOnClickListener(new EditButtonListener());
|
||||
this.connectivityFailureButton.setOnClickListener(new EditButtonListener());
|
||||
@ -159,9 +166,12 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
this.verificationCheck.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(FOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.retrieveDirectoryText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@ -175,9 +185,12 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
this.verificationCheck.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(FOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.retrieveDirectoryText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.VISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@ -192,13 +205,37 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
this.verificationCheck.setVisibility(View.VISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.VISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(FOCUSED_COLOR);
|
||||
this.retrieveDirectoryText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void handleStateRetrievingDirectory() {
|
||||
this.registrationLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.connectingProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingCheck.setVisibility(View.VISIBLE);
|
||||
this.verificationProgress.setVisibility(View.INVISIBLE);
|
||||
this.verificationCheck.setVisibility(View.VISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.VISIBLE);
|
||||
this.retrieveDirectoryCheck.setVisibility(View.INVISIBLE);
|
||||
this.retrieveDirectoryProgress.setVisibility(View.VISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.retrieveDirectoryText.setTextColor(FOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
|
||||
private void handleGcmTimeout(String number) {
|
||||
handleConnectivityError(number);
|
||||
}
|
||||
@ -294,15 +331,16 @@ public class RegistrationProgressActivity extends SherlockActivity {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case RegistrationState.STATE_IDLE: handleStateIdle(); break;
|
||||
case RegistrationState.STATE_CONNECTING: handleStateConnecting(); break;
|
||||
case RegistrationState.STATE_VERIFYING: handleStateVerifying(); break;
|
||||
case RegistrationState.STATE_TIMER: handleTimerUpdate(); break;
|
||||
case RegistrationState.STATE_GCM_REGISTERING: handleStateGcmRegistering(); break;
|
||||
case RegistrationState.STATE_TIMEOUT: handleVerificationTimeout((String)message.obj); break;
|
||||
case RegistrationState.STATE_COMPLETE: handleVerificationComplete(); break;
|
||||
case RegistrationState.STATE_GCM_TIMEOUT: handleGcmTimeout((String)message.obj); break;
|
||||
case RegistrationState.STATE_NETWORK_ERROR: handleConnectivityError((String)message.obj); break;
|
||||
case RegistrationState.STATE_IDLE: handleStateIdle(); break;
|
||||
case RegistrationState.STATE_CONNECTING: handleStateConnecting(); break;
|
||||
case RegistrationState.STATE_VERIFYING: handleStateVerifying(); break;
|
||||
case RegistrationState.STATE_TIMER: handleTimerUpdate(); break;
|
||||
case RegistrationState.STATE_GCM_REGISTERING: handleStateGcmRegistering(); break;
|
||||
case RegistrationState.STATE_RETRIEVING_DIRECTORY: handleStateRetrievingDirectory(); break;
|
||||
case RegistrationState.STATE_TIMEOUT: handleVerificationTimeout((String)message.obj); break;
|
||||
case RegistrationState.STATE_COMPLETE: handleVerificationComplete(); break;
|
||||
case RegistrationState.STATE_GCM_TIMEOUT: handleGcmTimeout((String)message.obj); break;
|
||||
case RegistrationState.STATE_NETWORK_ERROR: handleConnectivityError((String)message.obj); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,22 +94,22 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
|
||||
}
|
||||
|
||||
private void handleDisplayConversationOrList() {
|
||||
Intent intent = new Intent(this, RegistrationActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
|
||||
// ConversationParameters parameters = getConversationParameters();
|
||||
//
|
||||
// Intent intent;
|
||||
//
|
||||
// if (isShareAction() || parameters.recipients != null) {
|
||||
// intent = getConversationIntent(parameters);
|
||||
// } else {
|
||||
// intent = getConversationListIntent();
|
||||
// }
|
||||
//
|
||||
// Intent intent = new Intent(this, RegistrationActivity.class);
|
||||
// startActivity(intent);
|
||||
// finish();
|
||||
|
||||
ConversationParameters parameters = getConversationParameters();
|
||||
|
||||
Intent intent;
|
||||
|
||||
if (isShareAction() || parameters.recipients != null) {
|
||||
intent = getConversationIntent(parameters);
|
||||
} else {
|
||||
intent = getConversationListIntent();
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private Intent getConversationIntent(ConversationParameters parameters) {
|
||||
|
@ -26,7 +26,7 @@ import android.widget.Toast;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
|
||||
|
@ -23,7 +23,7 @@ import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.SerializableKey;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
|
@ -19,7 +19,7 @@ package org.thoughtcrime.securesms.crypto;
|
||||
import android.content.Context;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
public class AuthenticityCalculator {
|
||||
|
@ -22,7 +22,7 @@ import android.content.DialogInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
|
@ -19,7 +19,7 @@ package org.thoughtcrime.securesms.crypto;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.protocol.Message;
|
||||
import org.thoughtcrime.securesms.protocol.Prefix;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
@ -21,9 +21,9 @@ import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.protocol.Message;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
@ -29,9 +29,9 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.math.ec.ECCurve;
|
||||
import org.bouncycastle.math.ec.ECFieldElement;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import android.content.Context;
|
||||
|
@ -33,11 +33,11 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.thoughtcrime.securesms.database.InvalidKeyIdException;
|
||||
import org.thoughtcrime.securesms.database.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.SessionKey;
|
||||
import org.thoughtcrime.securesms.database.SessionRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.InvalidKeyIdException;
|
||||
import org.thoughtcrime.securesms.database.keys.LocalKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.RemoteKeyRecord;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionKey;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionRecord;
|
||||
import org.thoughtcrime.securesms.protocol.Message;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
|
@ -18,13 +18,13 @@ package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.protocol.Prefix;
|
||||
import org.thoughtcrime.securesms.sms.TextMessage;
|
||||
|
||||
public class EncryptingSmsDatabase extends SmsDatabase {
|
||||
|
||||
@ -65,12 +65,12 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
||||
return insertMessageSent(masterSecret, address, threadId, body, date, Types.ENCRYPTING_TYPE);
|
||||
}
|
||||
|
||||
public long insertMessageReceived(MasterSecret masterSecret, SmsMessage message, String body) {
|
||||
public long insertMessageReceived(MasterSecret masterSecret, TextMessage message, String body) {
|
||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||
return insertMessageReceived(message, encryptedBody);
|
||||
}
|
||||
|
||||
public long insertMessageReceived(AsymmetricMasterSecret masterSecret, SmsMessage message, String body) {
|
||||
public long insertMessageReceived(AsymmetricMasterSecret masterSecret, TextMessage message, String body) {
|
||||
String encryptedBody = getAsymmetricEncryptedBody(masterSecret, body);
|
||||
return insertSecureMessageReceived(message, encryptedBody);
|
||||
}
|
||||
|
@ -22,11 +22,11 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.TextMessage;
|
||||
import org.thoughtcrime.securesms.util.Trimmer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -84,18 +84,18 @@ public class SmsDatabase extends Database {
|
||||
notifyConversationListeners(getThreadIdForMessage(id));
|
||||
}
|
||||
|
||||
private long insertMessageReceived(SmsMessage message, String body, long type, long timeSent) {
|
||||
private long insertMessageReceived(TextMessage message, String body, long type, long timeSent) {
|
||||
List<Recipient> recipientList = new ArrayList<Recipient>(1);
|
||||
recipientList.add(new Recipient(null, message.getDisplayOriginatingAddress(), null, null));
|
||||
recipientList.add(new Recipient(null, message.getSender(), null, null));
|
||||
Recipients recipients = new Recipients(recipientList);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
|
||||
ContentValues values = new ContentValues(6);
|
||||
values.put(ADDRESS, message.getDisplayOriginatingAddress());
|
||||
values.put(ADDRESS, message.getSender());
|
||||
values.put(DATE_RECEIVED, Long.valueOf(System.currentTimeMillis()));
|
||||
values.put(DATE_SENT, timeSent);
|
||||
values.put(PROTOCOL, message.getProtocolIdentifier());
|
||||
values.put(PROTOCOL, message.getProtocol());
|
||||
values.put(READ, Integer.valueOf(0));
|
||||
|
||||
if (message.getPseudoSubject().length() > 0)
|
||||
@ -212,13 +212,13 @@ public class SmsDatabase extends Database {
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public long insertSecureMessageReceived(SmsMessage message, String body) {
|
||||
public long insertSecureMessageReceived(TextMessage message, String body) {
|
||||
return insertMessageReceived(message, body, Types.DECRYPT_IN_PROGRESS_TYPE,
|
||||
message.getTimestampMillis());
|
||||
message.getSentTimestampMillis());
|
||||
}
|
||||
|
||||
public long insertMessageReceived(SmsMessage message, String body) {
|
||||
return insertMessageReceived(message, body, Types.INBOX_TYPE, message.getTimestampMillis());
|
||||
public long insertMessageReceived(TextMessage message, String body) {
|
||||
return insertMessageReceived(message, body, Types.INBOX_TYPE, message.getSentTimestampMillis());
|
||||
}
|
||||
|
||||
public long insertMessageSent(String address, long threadId, String body, long date, long type) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
public class InvalidKeyIdException extends Exception {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
@ -24,6 +24,9 @@ import org.thoughtcrime.securesms.crypto.KeyPair;
|
||||
import org.thoughtcrime.securesms.crypto.KeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.thoughtcrime.securesms.database.keys.InvalidKeyIdException;
|
||||
import org.thoughtcrime.securesms.database.keys.Record;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.io.FileInputStream;
|
@ -14,7 +14,7 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
import android.content.Context;
|
||||
|
@ -14,13 +14,16 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.crypto.PublicKey;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.thoughtcrime.securesms.database.keys.InvalidKeyIdException;
|
||||
import org.thoughtcrime.securesms.database.keys.Record;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
|
@ -14,7 +14,7 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
@ -14,7 +14,7 @@
|
||||
* 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.database;
|
||||
package org.thoughtcrime.securesms.database.keys;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
@ -22,6 +22,9 @@ import android.util.Log;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.thoughtcrime.securesms.database.keys.Record;
|
||||
import org.thoughtcrime.securesms.database.keys.SessionKey;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.io.FileInputStream;
|
86
src/org/thoughtcrime/securesms/directory/BloomFilter.java
Normal file
86
src/org/thoughtcrime/securesms/directory/BloomFilter.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.directory;
|
||||
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* A simple bloom filter implementation that backs the RedPhone directory.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
|
||||
public class BloomFilter {
|
||||
|
||||
private final MappedByteBuffer buffer;
|
||||
private final long length;
|
||||
private final int hashCount;
|
||||
|
||||
public BloomFilter(File bloomFilter, int hashCount)
|
||||
throws IOException
|
||||
{
|
||||
this.length = bloomFilter.length();
|
||||
this.buffer = new FileInputStream(bloomFilter).getChannel()
|
||||
.map(FileChannel.MapMode.READ_ONLY, 0, length);
|
||||
this.hashCount = hashCount;
|
||||
}
|
||||
|
||||
public int getHashCount() {
|
||||
return hashCount;
|
||||
}
|
||||
|
||||
private boolean isBitSet(long bitIndex) {
|
||||
int byteInQuestion = this.buffer.get((int)(bitIndex / 8));
|
||||
int bitOffset = (0x01 << (bitIndex % 8));
|
||||
|
||||
return (byteInQuestion & bitOffset) > 0;
|
||||
}
|
||||
|
||||
public boolean contains(String entity) {
|
||||
try {
|
||||
for (int i=0;i<this.hashCount;i++) {
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(new SecretKeySpec((i+"").getBytes(), "HmacSHA1"));
|
||||
|
||||
byte[] hashValue = mac.doFinal(entity.getBytes());
|
||||
long bitIndex = Math.abs(Conversions.byteArrayToLong(hashValue, 0)) % (this.length * 8);
|
||||
|
||||
if (!isBitSet(bitIndex))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package org.thoughtcrime.securesms.directory;
|
||||
|
||||
public class DirectoryDescriptor {
|
||||
private String version;
|
||||
private long capacity;
|
||||
private int hashCount;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public int getHashCount() {
|
||||
return hashCount;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
private String url;
|
||||
|
||||
}
|
181
src/org/thoughtcrime/securesms/directory/NumberFilter.java
Normal file
181
src/org/thoughtcrime/securesms/directory/NumberFilter.java
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.directory;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.thoughtcrimegson.Gson;
|
||||
import com.google.thoughtcrimegson.JsonParseException;
|
||||
import com.google.thoughtcrimegson.annotations.SerializedName;
|
||||
import org.thoughtcrime.securesms.util.PhoneNumberFormatter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Handles providing lookups, serializing, and deserializing the RedPhone directory.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
|
||||
public class NumberFilter {
|
||||
|
||||
private static NumberFilter instance;
|
||||
|
||||
public synchronized static NumberFilter getInstance(Context context) {
|
||||
if (instance == null)
|
||||
instance = NumberFilter.deserializeFromFile(context);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static final String DIRECTORY_META_FILE = "directory.stat";
|
||||
|
||||
private File bloomFilter;
|
||||
private String version;
|
||||
private long capacity;
|
||||
private int hashCount;
|
||||
private Context context;
|
||||
|
||||
private NumberFilter(Context context, File bloomFilter, long capacity,
|
||||
int hashCount, String version)
|
||||
{
|
||||
this.context = context.getApplicationContext();
|
||||
this.bloomFilter = bloomFilter;
|
||||
this.capacity = capacity;
|
||||
this.hashCount = hashCount;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public synchronized boolean containsNumber(String number) {
|
||||
try {
|
||||
if (bloomFilter == null) return false;
|
||||
else if (number == null || number.length() == 0) return false;
|
||||
|
||||
return new BloomFilter(bloomFilter, hashCount).contains(PhoneNumberFormatter.formatNumber(context, number));
|
||||
} catch (IOException ioe) {
|
||||
Log.w("NumberFilter", ioe);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void update(File bloomFilter, long capacity, int hashCount, String version)
|
||||
{
|
||||
if (this.bloomFilter != null)
|
||||
this.bloomFilter.delete();
|
||||
|
||||
this.bloomFilter = bloomFilter;
|
||||
this.capacity = capacity;
|
||||
this.hashCount = hashCount;
|
||||
this.version = version;
|
||||
|
||||
serializeToFile(context);
|
||||
}
|
||||
|
||||
private void serializeToFile(Context context) {
|
||||
if (this.bloomFilter == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
FileOutputStream fout = context.openFileOutput(DIRECTORY_META_FILE, 0);
|
||||
NumberFilterStorage storage = new NumberFilterStorage(bloomFilter.getAbsolutePath(),
|
||||
capacity, hashCount, version);
|
||||
|
||||
storage.serializeToStream(fout);
|
||||
fout.close();
|
||||
} catch (IOException ioe) {
|
||||
Log.w("NumberFilter", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private static NumberFilter deserializeFromFile(Context context) {
|
||||
try {
|
||||
FileInputStream fis = context.openFileInput(DIRECTORY_META_FILE);
|
||||
NumberFilterStorage storage = NumberFilterStorage.fromStream(fis);
|
||||
|
||||
if (storage == null) return new NumberFilter(context, null, 0, 0, "0");
|
||||
else return new NumberFilter(context,
|
||||
new File(storage.getDataPath()),
|
||||
storage.getCapacity(),
|
||||
storage.getHashCount(),
|
||||
storage.getVersion());
|
||||
} catch (IOException ioe) {
|
||||
Log.w("NumberFilter", ioe);
|
||||
return new NumberFilter(context, null, 0, 0, "0");
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumberFilterStorage {
|
||||
@SerializedName("data_path")
|
||||
private String dataPath;
|
||||
|
||||
@SerializedName("capacity")
|
||||
private long capacity;
|
||||
|
||||
@SerializedName("hash_count")
|
||||
private int hashCount;
|
||||
|
||||
@SerializedName("version")
|
||||
private String version;
|
||||
|
||||
public NumberFilterStorage(String dataPath, long capacity, int hashCount, String version) {
|
||||
this.dataPath = dataPath;
|
||||
this.capacity = capacity;
|
||||
this.hashCount = hashCount;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getDataPath() {
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public int getHashCount() {
|
||||
return hashCount;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void serializeToStream(OutputStream out) throws IOException {
|
||||
out.write(new Gson().toJson(this).getBytes());
|
||||
}
|
||||
|
||||
public static NumberFilterStorage fromStream(InputStream in) throws IOException {
|
||||
try {
|
||||
return new Gson().fromJson(new BufferedReader(new InputStreamReader(in)),
|
||||
NumberFilterStorage.class);
|
||||
} catch (JsonParseException jpe) {
|
||||
Log.w("NumberFilter", jpe);
|
||||
throw new IOException("JSON Parse Exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,18 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gcm.GCMBaseIntentService;
|
||||
import com.google.thoughtcrimegson.Gson;
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.service.RegistrationService;
|
||||
import org.thoughtcrime.securesms.service.SendReceiveService;
|
||||
import org.thoughtcrime.securesms.sms.TextMessage;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GcmIntentService extends GCMBaseIntentService {
|
||||
|
||||
@ -21,25 +29,52 @@ public class GcmIntentService extends GCMBaseIntentService {
|
||||
intent.putExtra(RegistrationService.GCM_REGISTRATION_ID, registrationId);
|
||||
sendBroadcast(intent);
|
||||
} else {
|
||||
//
|
||||
// // Talk to the server directly.
|
||||
//
|
||||
try {
|
||||
getGcmSocket(context).registerGcmId(registrationId);
|
||||
} catch (IOException e) {
|
||||
Log.w("GcmIntentService", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnregistered(Context context, String registrationId) {
|
||||
try {
|
||||
getGcmSocket(context).unregisterGcmId(registrationId);
|
||||
} catch (IOException ioe) {
|
||||
Log.w("GcmIntentService", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onMessage(Context context, Intent intent) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
Log.w("GcmIntentService", "Got GCM message!");
|
||||
String data = intent.getStringExtra("message");
|
||||
Log.w("GcmIntentService", "GCM message: " + data);
|
||||
|
||||
if (Util.isEmpty(data))
|
||||
return;
|
||||
|
||||
IncomingGcmMessage message = new Gson().fromJson(data, IncomingGcmMessage.class);
|
||||
ArrayList<TextMessage> messages = new ArrayList<TextMessage>();
|
||||
messages.add(new TextMessage(message));
|
||||
|
||||
Intent receivedIntent = new Intent(context, SendReceiveService.class);
|
||||
receivedIntent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
|
||||
receivedIntent.putParcelableArrayListExtra("text_messages", messages);
|
||||
context.startService(receivedIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onError(Context context, String s) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
Log.w("GcmIntentService", "GCM Error: " + s);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onUnregistered(Context context, String s) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
private GcmSocket getGcmSocket(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String localNumber = preferences.getString(ApplicationPreferencesActivity.LOCAL_NUMBER_PREF, null);
|
||||
String password = preferences.getString(ApplicationPreferencesActivity.GCM_PASSWORD_PREF, null);
|
||||
return new GcmSocket(context, localNumber, password);
|
||||
}
|
||||
}
|
||||
|
18
src/org/thoughtcrime/securesms/gcm/GcmMessageResponse.java
Normal file
18
src/org/thoughtcrime/securesms/gcm/GcmMessageResponse.java
Normal file
@ -0,0 +1,18 @@
|
||||
package org.thoughtcrime.securesms.gcm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GcmMessageResponse {
|
||||
private List<String> success;
|
||||
private List<String> failure;
|
||||
|
||||
public List<String> getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public List<String> getFailure() {
|
||||
return failure;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package org.thoughtcrime.securesms.gcm;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
||||
public class GcmSender {
|
||||
|
||||
private static final GcmSender instance = new GcmSender();
|
||||
|
||||
public static GcmSender getDefault() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void sendTextMessage(String recipient, String text,
|
||||
PendingIntent sentIntent,
|
||||
PendingIntent deliveredIntent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -3,13 +3,18 @@ package org.thoughtcrime.securesms.gcm;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.thoughtcrimegson.Gson;
|
||||
import org.thoughtcrime.securesms.directory.DirectoryDescriptor;
|
||||
import org.thoughtcrime.securesms.directory.NumberFilter;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@ -21,18 +26,21 @@ import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class RegistrationSocket {
|
||||
public class GcmSocket {
|
||||
|
||||
private static final String CREATE_ACCOUNT_PATH = "/v1/accounts/%s";
|
||||
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/%s";
|
||||
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/%s";
|
||||
private static final String DIRECTORY_PATH = "/v1/directory/";
|
||||
private static final String MESSAGE_PATH = "/v1/messages/";
|
||||
|
||||
private final String localNumber;
|
||||
private final String password;
|
||||
private final TrustManagerFactory trustManagerFactory;
|
||||
|
||||
public RegistrationSocket(Context context, String localNumber, String password) {
|
||||
public GcmSocket(Context context, String localNumber, String password) {
|
||||
this.localNumber = localNumber;
|
||||
this.password = password;
|
||||
this.trustManagerFactory = initializeTrustManagerFactory(context);
|
||||
@ -54,6 +62,118 @@ public class RegistrationSocket {
|
||||
makeRequest(String.format(REGISTER_GCM_PATH, localNumber), "PUT", new Gson().toJson(registration));
|
||||
}
|
||||
|
||||
public void unregisterGcmId(String gcmRegistrationId) throws IOException {
|
||||
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId);
|
||||
makeRequest(String.format(REGISTER_GCM_PATH, localNumber), "DELETE", new Gson().toJson(registration));
|
||||
}
|
||||
|
||||
public void sendMessage(String recipient, String messageText) throws IOException {
|
||||
OutgoingGcmMessage message = new OutgoingGcmMessage(recipient, messageText);
|
||||
String responseText = makeRequest(MESSAGE_PATH, "POST", new Gson().toJson(message));
|
||||
GcmMessageResponse response = new Gson().fromJson(responseText, GcmMessageResponse.class);
|
||||
|
||||
if (response.getFailure().size() != 0)
|
||||
throw new IOException("Got send failure: " + response.getFailure().get(0));
|
||||
}
|
||||
|
||||
public void retrieveDirectory(Context context ) {
|
||||
try {
|
||||
DirectoryDescriptor directoryDescriptor = new Gson().fromJson(makeRequest(DIRECTORY_PATH, "GET", null),
|
||||
DirectoryDescriptor.class);
|
||||
|
||||
File directoryData = downloadExternalFile(context, directoryDescriptor.getUrl());
|
||||
|
||||
NumberFilter.getInstance(context).update(directoryData,
|
||||
directoryDescriptor.getCapacity(),
|
||||
directoryDescriptor.getHashCount(),
|
||||
directoryDescriptor.getVersion());
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.w("GcmSocket", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private File downloadExternalFile(Context context, String url) throws IOException {
|
||||
File download = File.createTempFile("directory", ".dat", context.getFilesDir());
|
||||
URL downloadUrl = new URL(url);
|
||||
HttpsURLConnection connection = (HttpsURLConnection)downloadUrl.openConnection();
|
||||
connection.setDoInput(true);
|
||||
|
||||
if (connection.getResponseCode() != 200) {
|
||||
throw new IOException("Bad response: " + connection.getResponseCode());
|
||||
}
|
||||
|
||||
OutputStream output = new FileOutputStream(download);
|
||||
InputStream input = new GZIPInputStream(connection.getInputStream());
|
||||
int read = 0;
|
||||
byte[] buffer = new byte[4096];
|
||||
|
||||
while ((read = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
output.close();
|
||||
|
||||
return download;
|
||||
}
|
||||
|
||||
private String makeRequest(String urlFragment, String method, String body) throws IOException {
|
||||
HttpsURLConnection connection = getConnection(urlFragment, method);
|
||||
|
||||
if (body != null) {
|
||||
connection.setDoOutput(true);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
if (body != null) {
|
||||
Log.w("GcmSocket", method + " -- " + body);
|
||||
OutputStream out = connection.getOutputStream();
|
||||
out.write(body.getBytes());
|
||||
out.close();
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() != 200) {
|
||||
throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
return Util.readFully(connection.getInputStream());
|
||||
}
|
||||
|
||||
private HttpsURLConnection getConnection(String urlFragment, String method) throws IOException {
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
|
||||
URL url = new URL(String.format("https://gcm.textsecure.whispersystems.org%s", urlFragment));
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setSSLSocketFactory(context.getSocketFactory());
|
||||
connection.setRequestMethod(method);
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
|
||||
if (password != null) {
|
||||
System.out.println("Adding authorization header: " + getAuthorizationHeader());
|
||||
connection.setRequestProperty("Authorization", getAuthorizationHeader());
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (KeyManagementException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAuthorizationHeader() {
|
||||
try {
|
||||
return "Basic " + new String(Base64.encode((localNumber + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private TrustManagerFactory initializeTrustManagerFactory(Context context) {
|
||||
try {
|
||||
AssetManager assetManager = context.getAssets();
|
||||
@ -77,54 +197,6 @@ public class RegistrationSocket {
|
||||
}
|
||||
}
|
||||
|
||||
private String makeRequest(String urlFragment, String method, String body) throws IOException {
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
|
||||
URL url = new URL(String.format("https://gcm.textsecure.whispersystems.org/%s", urlFragment));
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setSSLSocketFactory(context.getSocketFactory());
|
||||
connection.setRequestMethod(method);
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
|
||||
if (password != null) {
|
||||
connection.setRequestProperty("Authorization", getAuthorizationHeader());
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
connection.setDoOutput(true);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
if (body != null) {
|
||||
OutputStream out = connection.getOutputStream();
|
||||
out.write(body.getBytes());
|
||||
out.close();
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() != 200) {
|
||||
throw new IOException("Bad response: " + connection.getResponseCode());
|
||||
}
|
||||
|
||||
return Util.readFully(connection.getInputStream());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (KeyManagementException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAuthorizationHeader() {
|
||||
try {
|
||||
return "Basic " + new String(Base64.encode((localNumber + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class Verification {
|
||||
|
42
src/org/thoughtcrime/securesms/gcm/IncomingGcmMessage.java
Normal file
42
src/org/thoughtcrime/securesms/gcm/IncomingGcmMessage.java
Normal file
@ -0,0 +1,42 @@
|
||||
package org.thoughtcrime.securesms.gcm;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class IncomingGcmMessage {
|
||||
|
||||
private String source;
|
||||
private List<String> destinations;
|
||||
private String messageText;
|
||||
private List<String> attachments;
|
||||
private long timestamp;
|
||||
|
||||
public IncomingGcmMessage(String source, List<String> destinations, String messageText, List<String> attachments, long timestamp) {
|
||||
this.source = source;
|
||||
this.destinations = destinations;
|
||||
this.messageText = messageText;
|
||||
this.attachments = attachments;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getTimestampMillis() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public List<String> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public String getMessageText() {
|
||||
return messageText;
|
||||
}
|
||||
|
||||
public List<String> getDestinations() {
|
||||
return destinations;
|
||||
}
|
||||
|
||||
}
|
79
src/org/thoughtcrime/securesms/gcm/OptimizingTransport.java
Normal file
79
src/org/thoughtcrime/securesms/gcm/OptimizingTransport.java
Normal file
@ -0,0 +1,79 @@
|
||||
package org.thoughtcrime.securesms.gcm;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.telephony.SmsManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.directory.NumberFilter;
|
||||
import org.thoughtcrime.securesms.util.PhoneNumberFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class OptimizingTransport {
|
||||
|
||||
public static void sendTextMessage(Context context, String destinationAddress, String message,
|
||||
PendingIntent sentIntent, PendingIntent deliveredIntent)
|
||||
{
|
||||
Log.w("OptimzingTransport", "Outgoing message: " + PhoneNumberFormatter.formatNumber(context, destinationAddress));
|
||||
NumberFilter filter = NumberFilter.getInstance(context);
|
||||
|
||||
if (filter.containsNumber(PhoneNumberFormatter.formatNumber(context, destinationAddress))) {
|
||||
Log.w("OptimzingTransport", "In the filter, sending GCM...");
|
||||
sendGcmTextMessage(context, destinationAddress, message, sentIntent, deliveredIntent);
|
||||
} else {
|
||||
Log.w("OptimzingTransport", "Not in the filter, sending SMS...");
|
||||
sendSmsTextMessage(destinationAddress, message, sentIntent, deliveredIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMultipartTextMessage(Context context,
|
||||
String recipient,
|
||||
ArrayList<String> messages,
|
||||
ArrayList<PendingIntent> sentIntents,
|
||||
ArrayList<PendingIntent> deliveredIntents)
|
||||
{
|
||||
// FIXME
|
||||
|
||||
sendTextMessage(context, recipient, messages.get(0), sentIntents.get(0), deliveredIntents == null ? null : deliveredIntents.get(0));
|
||||
}
|
||||
|
||||
|
||||
private static void sendGcmTextMessage(Context context, String recipient, String messageText,
|
||||
PendingIntent sentIntent, PendingIntent deliveredIntent)
|
||||
{
|
||||
try {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String localNumber = preferences.getString(ApplicationPreferencesActivity.LOCAL_NUMBER_PREF, null);
|
||||
String password = preferences.getString(ApplicationPreferencesActivity.GCM_PASSWORD_PREF, null);
|
||||
|
||||
if (localNumber == null || password == null) {
|
||||
Log.w("OptimzingTransport", "No credentials, falling back to SMS...");
|
||||
sendSmsTextMessage(recipient, messageText, sentIntent, deliveredIntent);
|
||||
return;
|
||||
}
|
||||
|
||||
GcmSocket gcmSocket = new GcmSocket(context, localNumber, password);
|
||||
gcmSocket.sendMessage(PhoneNumberFormatter.formatNumber(context, recipient), messageText);
|
||||
sentIntent.send(Activity.RESULT_OK);
|
||||
} catch (IOException ioe) {
|
||||
Log.w("OptimizingTransport", ioe);
|
||||
Log.w("OptimzingTransport", "IOException, falling back to SMS...");
|
||||
sendSmsTextMessage(recipient, messageText, sentIntent, deliveredIntent);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.w("OptimizingTransport", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendSmsTextMessage(String recipient, String message,
|
||||
PendingIntent sentIntent, PendingIntent deliveredIntent)
|
||||
{
|
||||
SmsManager.getDefault().sendTextMessage(recipient, null, message, sentIntent, deliveredIntent);
|
||||
}
|
||||
|
||||
}
|
37
src/org/thoughtcrime/securesms/gcm/OutgoingGcmMessage.java
Normal file
37
src/org/thoughtcrime/securesms/gcm/OutgoingGcmMessage.java
Normal file
@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.gcm;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class OutgoingGcmMessage {
|
||||
|
||||
private List<String> destinations;
|
||||
private String messageText;
|
||||
private List<String> attachments;
|
||||
|
||||
public OutgoingGcmMessage(List<String> destinations, String messageText, List<String> attachments) {
|
||||
this.destinations = destinations;
|
||||
this.messageText = messageText;
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
public OutgoingGcmMessage(String destination, String messageText) {
|
||||
this.destinations = new LinkedList<String>();
|
||||
this.destinations.add(destination);
|
||||
this.messageText = messageText;
|
||||
this.attachments = new LinkedList<String>();
|
||||
}
|
||||
|
||||
public List<String> getDestinations() {
|
||||
return destinations;
|
||||
}
|
||||
|
||||
public String getMessageText() {
|
||||
return messageText;
|
||||
}
|
||||
|
||||
public List<String> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ import com.google.android.gcm.GCMRegistrar;
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.gcm.GcmIntentService;
|
||||
import org.thoughtcrime.securesms.gcm.GcmRegistrationTimeoutException;
|
||||
import org.thoughtcrime.securesms.gcm.RegistrationSocket;
|
||||
import org.thoughtcrime.securesms.gcm.GcmSocket;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -127,14 +127,14 @@ public class RegistrationService extends Service {
|
||||
registerReceiver(gcmRegistrationReceiver, filter);
|
||||
}
|
||||
|
||||
private void shutdownChallengeListener() {
|
||||
private synchronized void shutdownChallengeListener() {
|
||||
if (challengeReceiver != null) {
|
||||
unregisterReceiver(challengeReceiver);
|
||||
challengeReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownGcmRegistrationListener() {
|
||||
private synchronized void shutdownGcmRegistrationListener() {
|
||||
if (gcmRegistrationReceiver != null) {
|
||||
unregisterReceiver(gcmRegistrationReceiver);
|
||||
gcmRegistrationReceiver = null;
|
||||
@ -144,7 +144,7 @@ public class RegistrationService extends Service {
|
||||
private void handleRegistrationIntent(Intent intent) {
|
||||
markAsVerifying(true);
|
||||
|
||||
RegistrationSocket socket;
|
||||
GcmSocket socket;
|
||||
String number = intent.getStringExtra("e164number");
|
||||
|
||||
try {
|
||||
@ -153,7 +153,7 @@ public class RegistrationService extends Service {
|
||||
initializeGcmRegistrationListener();
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
|
||||
socket = new RegistrationSocket(this, number, password);
|
||||
socket = new GcmSocket(this, number, password);
|
||||
socket.createAccount();
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));
|
||||
@ -165,6 +165,9 @@ public class RegistrationService extends Service {
|
||||
String gcmRegistrationId = waitForGcmRegistrationId();
|
||||
socket.registerGcmId(gcmRegistrationId);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_RETRIEVING_DIRECTORY, number));
|
||||
socket.retrieveDirectory(this);
|
||||
|
||||
markAsVerified(number, password);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
|
||||
@ -315,6 +318,8 @@ public class RegistrationService extends Service {
|
||||
public static final int STATE_GCM_REGISTERING = 9;
|
||||
public static final int STATE_GCM_TIMEOUT = 10;
|
||||
|
||||
public static final int STATE_RETRIEVING_DIRECTORY = 11;
|
||||
|
||||
public final int state;
|
||||
public final String number;
|
||||
|
||||
|
@ -63,11 +63,11 @@ public class SendReceiveService extends Service {
|
||||
|
||||
private ToastHandler toastHandler;
|
||||
|
||||
private SmsReceiver smsReceiver;
|
||||
private SmsSender smsSender;
|
||||
private MmsReceiver mmsReceiver;
|
||||
private MmsSender mmsSender;
|
||||
private MmsDownloader mmsDownloader;
|
||||
private SmsReceiver smsReceiver;
|
||||
private SmsSender smsSender;
|
||||
private MmsReceiver mmsReceiver;
|
||||
private MmsSender mmsSender;
|
||||
private MmsDownloader mmsDownloader;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private boolean hasSecret;
|
||||
|
@ -27,6 +27,9 @@ import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||
import org.thoughtcrime.securesms.sms.TextMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SmsListener extends BroadcastReceiver {
|
||||
|
||||
@ -74,6 +77,16 @@ public class SmsListener extends BroadcastReceiver {
|
||||
return bodyBuilder.toString();
|
||||
}
|
||||
|
||||
private ArrayList<TextMessage> getAsTextMessages(Intent intent) {
|
||||
Object[] pdus = (Object[])intent.getExtras().get("pdus");
|
||||
ArrayList<TextMessage> messages = new ArrayList<TextMessage>(pdus.length);
|
||||
|
||||
for (int i=0;i<pdus.length;i++)
|
||||
messages.add(new TextMessage(SmsMessage.createFromPdu((byte[])pdus[i])));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private boolean isRelevant(Context context, Intent intent) {
|
||||
SmsMessage message = getSmsMessageFromIntent(intent);
|
||||
String messageBody = getSmsMessageBodyFromIntent(intent);
|
||||
@ -131,10 +144,11 @@ public class SmsListener extends BroadcastReceiver {
|
||||
|
||||
abortBroadcast();
|
||||
} else if (intent.getAction().equals(SMS_RECEIVED_ACTION) && isRelevant(context, intent)) {
|
||||
intent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
|
||||
intent.putExtra("ResultCode", this.getResultCode());
|
||||
intent.setClass(context, SendReceiveService.class);
|
||||
context.startService(intent);
|
||||
Intent receivedIntent = new Intent(context, SendReceiveService.class);
|
||||
receivedIntent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
|
||||
receivedIntent.putExtra("ResultCode", this.getResultCode());
|
||||
receivedIntent.putParcelableArrayListExtra("text_messages",getAsTextMessages(intent));
|
||||
context.startService(receivedIntent);
|
||||
|
||||
abortBroadcast();
|
||||
} else if (intent.getAction().equals(SendReceiveService.SENT_SMS_ACTION)) {
|
||||
|
@ -18,9 +18,7 @@ package org.thoughtcrime.securesms.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
@ -37,6 +35,9 @@ import org.thoughtcrime.securesms.protocol.Prefix;
|
||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MultipartMessageHandler;
|
||||
import org.thoughtcrime.securesms.sms.TextMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SmsReceiver {
|
||||
|
||||
@ -70,41 +71,42 @@ public class SmsReceiver {
|
||||
|
||||
}
|
||||
|
||||
private String assembleMessageFragments(SmsMessage[] messages) {
|
||||
private String assembleMessageFragments(TextMessage[] messages) {
|
||||
StringBuilder body = new StringBuilder();
|
||||
|
||||
for (SmsMessage message : messages) {
|
||||
body.append(message.getDisplayMessageBody());
|
||||
for (TextMessage message : messages) {
|
||||
body.append(message.getMessage());
|
||||
}
|
||||
|
||||
String messageBody = body.toString();
|
||||
|
||||
if (WirePrefix.isEncryptedMessage(messageBody) || WirePrefix.isKeyExchange(messageBody)) {
|
||||
return assembleSecureMessageFragments(messages[0].getDisplayOriginatingAddress(), messageBody);
|
||||
return assembleSecureMessageFragments(messages[0].getSender(), messageBody);
|
||||
} else {
|
||||
return messageBody;
|
||||
}
|
||||
}
|
||||
|
||||
private long storeSecureMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
|
||||
private long storeSecureMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
|
||||
long messageId = DatabaseFactory.getSmsDatabase(context).insertSecureMessageReceived(message, messageBody);
|
||||
Log.w("SmsReceiver", "Inserted secure message received: " + messageId);
|
||||
|
||||
if (masterSecret != null)
|
||||
DecryptingQueue.scheduleDecryption(context, masterSecret, messageId, message.getDisplayOriginatingAddress(), messageBody);
|
||||
DecryptingQueue.scheduleDecryption(context, masterSecret, messageId, message.getSender(), messageBody);
|
||||
|
||||
return messageId;
|
||||
}
|
||||
|
||||
private long storeStandardMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
|
||||
private long storeStandardMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
|
||||
if (masterSecret != null) return DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageReceived(masterSecret, message, messageBody);
|
||||
else if (MasterSecretUtil.hasAsymmericMasterSecret(context)) return DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageReceived(MasterSecretUtil.getAsymmetricMasterSecret(context, null), message, messageBody);
|
||||
else return DatabaseFactory.getSmsDatabase(context).insertMessageReceived(message, messageBody);
|
||||
}
|
||||
|
||||
private long storeKeyExchangeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
|
||||
private long storeKeyExchangeMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) {
|
||||
try {
|
||||
Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null, null);
|
||||
Recipient recipient = new Recipient(null, message.getSender(), null, null);
|
||||
KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(messageBody);
|
||||
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
|
||||
|
||||
@ -132,7 +134,7 @@ public class SmsReceiver {
|
||||
return storeStandardMessage(masterSecret, message, messageBody);
|
||||
}
|
||||
|
||||
private long storeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
|
||||
private long storeMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
|
||||
if (messageBody.startsWith(Prefix.ASYMMETRIC_ENCRYPT)) {
|
||||
return storeSecureMessage(masterSecret, message, messageBody);
|
||||
} else if (messageBody.startsWith(Prefix.KEY_EXCHANGE)) {
|
||||
@ -142,19 +144,21 @@ public class SmsReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
private SmsMessage[] parseMessages(Bundle bundle) {
|
||||
Object[] pdus = (Object[])bundle.get("pdus");
|
||||
SmsMessage[] messages = new SmsMessage[pdus.length];
|
||||
|
||||
for (int i=0;i<pdus.length;i++)
|
||||
messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
|
||||
|
||||
return messages;
|
||||
}
|
||||
// private SmsMessage[] parseMessages(Bundle bundle) {
|
||||
// Object[] pdus = (Object[])bundle.get("pdus");
|
||||
// SmsMessage[] messages = new SmsMessage[pdus.length];
|
||||
//
|
||||
// for (int i=0;i<pdus.length;i++)
|
||||
// messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
|
||||
//
|
||||
// return messages;
|
||||
// }
|
||||
|
||||
private void handleReceiveMessage(MasterSecret masterSecret, Intent intent) {
|
||||
Bundle bundle = intent.getExtras();
|
||||
SmsMessage[] messages = parseMessages(bundle);
|
||||
ArrayList<TextMessage> messagesList = intent.getExtras().getParcelableArrayList("text_messages");
|
||||
TextMessage[] messages = messagesList.toArray(new TextMessage[0]);
|
||||
// Bundle bundle = intent.getExtras();
|
||||
// SmsMessage[] messages = parseMessages(bundle);
|
||||
String message = assembleMessageFragments(messages);
|
||||
|
||||
if (message != null) {
|
||||
|
@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.gcm.OptimizingTransport;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.protocol.KeyExchangeWirePrefix;
|
||||
import org.thoughtcrime.securesms.protocol.Prefix;
|
||||
@ -224,7 +225,9 @@ public class SmsSender {
|
||||
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
||||
// the app.
|
||||
try {
|
||||
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
|
||||
OptimizingTransport.sendMultipartTextMessage(context, recipient, messages, sentIntents, deliveredIntents);
|
||||
//
|
||||
// SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.w("SmsSender", npe);
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||
@ -257,8 +260,10 @@ public class SmsSender {
|
||||
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
||||
// the app.
|
||||
try {
|
||||
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
|
||||
deliveredIntents == null ? null : deliveredIntents.get(i));
|
||||
OptimizingTransport.sendTextMessage(context, recipient, messages.get(i), sentIntents.get(i),
|
||||
deliveredIntents == null ? null : deliveredIntents.get(i));
|
||||
// SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
|
||||
// deliveredIntents == null ? null : deliveredIntents.get(i));
|
||||
} catch (NullPointerException npe) {
|
||||
Log.w("SmsSender", npe);
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||
|
104
src/org/thoughtcrime/securesms/sms/TextMessage.java
Normal file
104
src/org/thoughtcrime/securesms/sms/TextMessage.java
Normal file
@ -0,0 +1,104 @@
|
||||
package org.thoughtcrime.securesms.sms;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import org.thoughtcrime.securesms.gcm.IncomingGcmMessage;
|
||||
|
||||
public class TextMessage implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<TextMessage> CREATOR = new Parcelable.Creator<TextMessage>() {
|
||||
@Override
|
||||
public TextMessage createFromParcel(Parcel in) {
|
||||
return new TextMessage(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextMessage[] newArray(int size) {
|
||||
return new TextMessage[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final String message;
|
||||
private final String sender;
|
||||
private final int protocol;
|
||||
private final String serviceCenterAddress;
|
||||
private final boolean replyPathPresent;
|
||||
private final String pseudoSubject;
|
||||
private final long sentTimestampMillis;
|
||||
|
||||
public TextMessage(SmsMessage message) {
|
||||
this.message = message.getDisplayMessageBody();
|
||||
this.sender = message.getDisplayOriginatingAddress();
|
||||
this.protocol = message.getProtocolIdentifier();
|
||||
this.serviceCenterAddress = message.getServiceCenterAddress();
|
||||
this.replyPathPresent = message.isReplyPathPresent();
|
||||
this.pseudoSubject = message.getPseudoSubject();
|
||||
this.sentTimestampMillis = message.getTimestampMillis();
|
||||
}
|
||||
|
||||
public TextMessage(IncomingGcmMessage message) {
|
||||
this.message = message.getMessageText();
|
||||
this.sender = message.getSource();
|
||||
this.protocol = 31337;
|
||||
this.serviceCenterAddress = "GCM";
|
||||
this.replyPathPresent = true;
|
||||
this.pseudoSubject = "";
|
||||
this.sentTimestampMillis = message.getTimestampMillis();
|
||||
}
|
||||
|
||||
public TextMessage(Parcel in) {
|
||||
this.message = in.readString();
|
||||
this.sender = in.readString();
|
||||
this.protocol = in.readInt();
|
||||
this.serviceCenterAddress = in.readString();
|
||||
this.replyPathPresent = (in.readInt() == 1);
|
||||
this.pseudoSubject = in.readString();
|
||||
this.sentTimestampMillis = in.readLong();
|
||||
}
|
||||
|
||||
public long getSentTimestampMillis() {
|
||||
return sentTimestampMillis;
|
||||
}
|
||||
|
||||
public String getPseudoSubject() {
|
||||
return pseudoSubject;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public String getServiceCenterAddress() {
|
||||
return serviceCenterAddress;
|
||||
}
|
||||
|
||||
public boolean isReplyPathPresent() {
|
||||
return replyPathPresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(message);
|
||||
out.writeString(sender);
|
||||
out.writeInt(protocol);
|
||||
out.writeString(serviceCenterAddress);
|
||||
out.writeInt(replyPathPresent ? 1 : 0);
|
||||
out.writeString(pseudoSubject);
|
||||
out.writeLong(sentTimestampMillis);
|
||||
}
|
||||
}
|
@ -158,6 +158,14 @@ public class Conversions {
|
||||
return byteArrayToLong(bytes, 0);
|
||||
}
|
||||
|
||||
public static long byteArray4ToLong(byte[] bytes, int offset) {
|
||||
return
|
||||
((bytes[offset + 0] & 0xffL) << 24) |
|
||||
((bytes[offset + 1] & 0xffL) << 16) |
|
||||
((bytes[offset + 2] & 0xffL) << 8) |
|
||||
((bytes[offset + 3] & 0xffL));
|
||||
}
|
||||
|
||||
public static long byteArrayToLong(byte[] bytes, int offset) {
|
||||
return
|
||||
((bytes[offset] & 0xffL) << 56) |
|
||||
|
Loading…
Reference in New Issue
Block a user