Bug 1212679 - Handle KitKat default messaging app intents. r=fabrice r=snorp

This commit is contained in:
Reuben Morais 2015-12-08 14:43:09 -05:00
parent f864c25f7f
commit 079e40fcb3
13 changed files with 481 additions and 66 deletions

View File

@ -24,14 +24,16 @@ namespace mozilla {
/*static*/
void
SmsManager::NotifySmsReceived(jni::String::Param aSender,
SmsManager::NotifySmsReceived(int32_t aId,
jni::String::Param aSender,
jni::String::Param aBody,
int32_t aMessageClass,
int64_t aSentTimestamp,
int64_t aTimestamp)
{
// TODO Need to correct the message `threadId` parameter value. Bug 859098
SmsMessageData message;
message.id() = 0;
message.id() = aId;
message.threadId() = 0;
message.iccId() = EmptyString();
message.delivery() = eDeliveryState_Received;
@ -41,7 +43,7 @@ SmsManager::NotifySmsReceived(jni::String::Param aSender,
message.body() = aBody ? nsString(aBody) : EmptyString();
message.messageClass() = static_cast<MessageClass>(aMessageClass);
message.timestamp() = aTimestamp;
message.sentTimestamp() = aTimestamp;
message.sentTimestamp() = aSentTimestamp;
message.deliveryTimestamp() = aTimestamp;
message.read() = false;

View File

@ -16,9 +16,11 @@ private:
SmsManager();
public:
static void NotifySmsReceived(jni::String::Param aSender,
static void NotifySmsReceived(int32_t aId,
jni::String::Param aSender,
jni::String::Param aBody,
int32_t aMessageClass,
int64_t aSentTimestamp,
int64_t aTimestamp);
static void NotifySmsSent(int32_t aId,
jni::String::Param aReceiver,

View File

@ -6,7 +6,12 @@ ANDROID_MANIFEST_FILE := src/main/AndroidManifest.xml
JAVAFILES := \
src/main/java/org/mozilla/b2gdroid/Apps.java \
src/main/java/org/mozilla/b2gdroid/GeckoEventReceiver.java \
src/main/java/org/mozilla/b2gdroid/HeadlessSmsSendService.java \
src/main/java/org/mozilla/b2gdroid/Launcher.java \
src/main/java/org/mozilla/b2gdroid/MmsService.java \
src/main/java/org/mozilla/b2gdroid/NotificationObserver.java \
src/main/java/org/mozilla/b2gdroid/RemoteGeckoEventProxy.java \
src/main/java/org/mozilla/b2gdroid/ScreenStateObserver.java \
src/main/java/org/mozilla/b2gdroid/SettingsMapper.java \
src/main/java/org/mozilla/b2gdroid/GeckoEventReceiver.java \

View File

@ -33,6 +33,9 @@
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.BROADCAST_SMS"/>
<uses-permission android:name="android.permission.WAP_PUSH_DELIVER"/>
<uses-permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
<uses-feature android:name="android.hardware.telephony"/>
@ -69,6 +72,37 @@
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true"/>
<!-- Listen for incoming SMS messages -->
<receiver android:name="org.mozilla.gecko.GeckoSmsManager"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>
</receiver>
<!-- Listen for incoming MMS messages -->
<receiver android:name=".MmsService"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<!-- Service that delivers messages from the phone "quick response" -->
<service android:name=".HeadlessSmsSendService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<activity android:name="org.mozilla.b2gdroid.Launcher"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:icon="@drawable/b2g"
@ -84,6 +118,18 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- Handle SMS intents -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
<!-- Default browser intents -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View File

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.b2gdroid;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import org.mozilla.gecko.GeckoAppShell;
public class HeadlessSmsSendService extends IntentService {
private final static String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
public HeadlessSmsSendService() {
super("HeadlessSmsSendService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!ACTION_RESPOND_VIA_MESSAGE.equals(intent.getAction())) {
return;
}
Bundle extras = intent.getExtras();
if (extras == null) {
return;
}
String recipient = intent.getData().getPath();
String message = extras.getString(Intent.EXTRA_TEXT);
if (recipient.length() == 0 || message.length() == 0) {
return;
}
GeckoAppShell.sendMessage(recipient, message, 0, /* shouldNotify */ false);
}
}

View File

@ -235,7 +235,7 @@ public class Launcher extends FragmentActivity
try {
obj.put("action", "view");
obj.put("url", intent.getDataString());
} catch(Exception ex) {
} catch(JSONException ex) {
Log.wtf(LOGTAG, "Error building Android:Launcher view message", ex);
}
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
@ -269,6 +269,21 @@ public class Launcher extends FragmentActivity
}
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
GeckoAppShell.sendEventToGecko(e);
} else if (Intent.ACTION_SENDTO.equals(action) ||
Intent.ACTION_SEND.equals(action)) {
Log.d(LOGTAG, "Sending new SMS intent to Gecko");
JSONObject obj = new JSONObject();
try {
obj.put("action", "send_sms");
if (Intent.ACTION_SENDTO.equals(action)) {
obj.put("number", intent.getData().getPath());
}
obj.put("body", intent.getStringExtra("sms_body"));
} catch (JSONException ex) {
Log.wtf(LOGTAG, "Error building Android:Launcher message", ex);
}
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
GeckoAppShell.sendEventToGecko(e);
}
}

View File

@ -0,0 +1,202 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.b2gdroid;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Build;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.util.Log;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_DELIVERY_IND;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.DeliveryInd;
import com.google.android.mms.pdu.GenericPdu;
import com.google.android.mms.pdu.NotificationInd;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduParser;
import com.google.android.mms.pdu.PduPersister;
import com.google.android.mms.pdu.ReadOrigInd;
import com.google.android.mms.util.SqliteWrapper;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoSmsManager;
public class MmsService extends BroadcastReceiver {
private final static String LOGTAG = "MmsService";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION)) {
final byte[] data = intent.getByteArrayExtra("data");
writeInboxMessage(data);
}
}
private static boolean shouldParseContentDisposition() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return SmsManager.getDefault().getCarrierConfigValues().getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
} else {
return false;
}
}
private void writeInboxMessage(byte[] pushData) {
final Context context = GeckoAppShell.getContext();
final GenericPdu pdu = new PduParser(pushData, shouldParseContentDisposition()).parse();
if (pdu == null) {
Log.e(LOGTAG, "Invalid PUSH PDU");
}
final PduPersister persister = PduPersister.getPduPersister(context);
final int type = pdu.getMessageType();
try {
switch (type) {
case MESSAGE_TYPE_DELIVERY_IND:
case MESSAGE_TYPE_READ_ORIG_IND: {
final long threadId = getDeliveryOrReadReportThreadId(context, pdu);
if (threadId == -1) {
// The associated SendReq isn't found, therefore skip
// processing this PDU.
Log.e(LOGTAG, "Failed to find delivery or read report's thread id");
break;
}
final Uri uri = persister.persist(
pdu,
Telephony.Mms.Inbox.CONTENT_URI,
true/*createThreadId*/,
true/*groupMmsEnabled*/,
null/*preOpenedFiles*/);
if (uri == null) {
Log.e(LOGTAG, "Failed to persist delivery or read report");
break;
}
// Update thread ID for ReadOrigInd & DeliveryInd.
final ContentValues values = new ContentValues(1);
values.put(Telephony.Mms.THREAD_ID, threadId);
if (SqliteWrapper.update(
context,
context.getContentResolver(),
uri,
values,
null/*where*/,
null/*selectionArgs*/) != 1) {
Log.e(LOGTAG, "Failed to update delivery or read report thread id");
}
break;
}
case MESSAGE_TYPE_NOTIFICATION_IND: {
final NotificationInd nInd = (NotificationInd) pdu;
if (!isDuplicateNotification(context, nInd)) {
final Uri uri = persister.persist(
pdu,
Telephony.Mms.Inbox.CONTENT_URI,
true/*createThreadId*/,
true/*groupMmsEnabled*/,
null/*preOpenedFiles*/);
if (uri == null) {
Log.e(LOGTAG, "Failed to save MMS WAP push notification ind");
}
} else {
Log.d(LOGTAG, "Skip storing duplicate MMS WAP push notification ind: "
+ new String(nInd.getContentLocation()));
}
break;
}
default:
Log.e(LOGTAG, "Received unrecognized WAP Push PDU.");
}
} catch (MmsException e) {
Log.e(LOGTAG, "Failed to save MMS WAP push data: type=" + type, e);
} catch (RuntimeException e) {
Log.e(LOGTAG, "Unexpected RuntimeException in persisting MMS WAP push data", e);
}
}
private static final String THREAD_ID_SELECTION =
Telephony.Mms.MESSAGE_ID + "=? AND " + Telephony.Mms.MESSAGE_TYPE + "=?";
private static long getDeliveryOrReadReportThreadId(Context context, GenericPdu pdu) {
String messageId;
if (pdu instanceof DeliveryInd) {
messageId = new String(((DeliveryInd) pdu).getMessageId());
} else if (pdu instanceof ReadOrigInd) {
messageId = new String(((ReadOrigInd) pdu).getMessageId());
} else {
Log.e(LOGTAG, "WAP Push data is neither delivery or read report type: "
+ pdu.getClass().getCanonicalName());
return -1L;
}
Cursor cursor = null;
try {
cursor = SqliteWrapper.query(
context,
context.getContentResolver(),
Telephony.Mms.CONTENT_URI,
new String[]{ Telephony.Mms.THREAD_ID },
THREAD_ID_SELECTION,
new String[]{
DatabaseUtils.sqlEscapeString(messageId),
Integer.toString(PduHeaders.MESSAGE_TYPE_SEND_REQ)
},
null/*sortOrder*/);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(0);
}
} catch (SQLiteException e) {
Log.e(LOGTAG, "Failed to query delivery or read report thread id", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return -1L;
}
private static final String LOCATION_SELECTION =
Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?";
private static boolean isDuplicateNotification(Context context, NotificationInd nInd) {
final byte[] rawLocation = nInd.getContentLocation();
if (rawLocation != null) {
String location = new String(rawLocation);
String[] selectionArgs = new String[] { location };
Cursor cursor = null;
try {
cursor = SqliteWrapper.query(
context,
context.getContentResolver(),
Telephony.Mms.CONTENT_URI,
new String[]{Telephony.Mms._ID},
LOCATION_SELECTION,
new String[]{
Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
new String(rawLocation)
},
null/*sortOrder*/);
if (cursor != null && cursor.getCount() > 0) {
// We already received the same notification before.
return true;
}
} catch (SQLiteException e) {
Log.e(LOGTAG, "failed to query existing notification ind", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
return false;
}
}

View File

@ -105,6 +105,16 @@ this.MessagesBridge = {
data: { type: "url",
url: data.url } });
break;
case "send_sms":
new window.MozActivity({
name: "new",
data: {
type: "websms/sms",
number: data.number,
body: data.body
}
});
break;
case "home-key":
window.dispatchEvent(new window.KeyboardEvent("keydown", { key: "Home" }));
window.dispatchEvent(new window.KeyboardEvent("keyup", { key: "Home" }));

View File

@ -2319,13 +2319,17 @@ public class GeckoAppShell
/*
* WebSMS related methods.
*/
@WrapForJNI(stubName = "SendMessageWrapper")
public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
public static void sendMessage(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify) {
if (!SmsManager.isEnabled()) {
return;
}
SmsManager.getInstance().send(aNumber, aMessage, aRequestId);
SmsManager.getInstance().send(aNumber, aMessage, aRequestId, aShouldNotify);
}
@WrapForJNI(stubName = "SendMessageWrapper")
public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
sendMessage(aNumber, aMessage, aRequestId, /* shouldNotify */ true);
}
@WrapForJNI(stubName = "GetMessageWrapper")

View File

@ -21,6 +21,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
@ -267,7 +268,6 @@ public class GeckoSmsManager
extends BroadcastReceiver
implements ISmsManager
{
public final static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
public final static String ACTION_SMS_SENT = "org.mozilla.gecko.SMS_SENT";
public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED";
@ -297,12 +297,12 @@ public class GeckoSmsManager
private final static int kMaxMessageSize = 160;
private final static Uri kSmsContentUri = Uri.parse("content://sms");
private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
private final static Uri kSmsThreadsContentUri = Uri.parse("content://sms/conversations");
private final static Uri kSmsContentUri = Telephony.Sms.Inbox.CONTENT_URI;
private final static Uri kSmsSentContentUri = Telephony.Sms.Sent.CONTENT_URI;
private final static Uri kSmsThreadsContentUri = Telephony.Sms.Conversations.CONTENT_URI;
private final static int kSmsTypeInbox = 1;
private final static int kSmsTypeSentbox = 2;
private final static int kSmsTypeInbox = Telephony.Sms.MESSAGE_TYPE_INBOX;
private final static int kSmsTypeSentbox = Telephony.Sms.MESSAGE_TYPE_SENT;
/*
* Keep the following state codes in syng with |DeliveryState| in:
@ -325,15 +325,6 @@ public class GeckoSmsManager
private final static int kDeliveryStatusPending = 2;
private final static int kDeliveryStatusError = 3;
/*
* android.provider.Telephony.Sms.STATUS_*. Duplicated because they're not
* part of Android public API.
*/
private final static int kInternalDeliveryStatusNone = -1;
private final static int kInternalDeliveryStatusComplete = 0;
private final static int kInternalDeliveryStatusPending = 32;
private final static int kInternalDeliveryStatusFailed = 64;
/*
* Keep the following values in sync with |MessageClass| in:
* dom/mobilemessage/Types.h
@ -355,45 +346,91 @@ public class GeckoSmsManager
private static final long UNSIGNED_INTEGER_MAX_VALUE = Integer.MAX_VALUE * 2L + 1L;
public GeckoSmsManager() {
SmsIOThread.getInstance().start();
if (SmsIOThread.getInstance().getState() == Thread.State.NEW) {
SmsIOThread.getInstance().start();
}
}
private boolean isDefaultSmsApp(Context context) {
String myPackageName = context.getPackageName();
return Telephony.Sms.getDefaultSmsPackage(context).equals(myPackageName);
}
@Override
public void start() {
IntentFilter smsFilter = new IntentFilter();
smsFilter.addAction(ACTION_SMS_RECEIVED);
smsFilter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
smsFilter.addAction(ACTION_SMS_SENT);
smsFilter.addAction(ACTION_SMS_DELIVERED);
GeckoAppShell.getContext().registerReceiver(this, smsFilter);
}
/**
* Build up the SMS message body from the SmsMessage array of received SMS
*
* @param msgs The SmsMessage array of the received SMS
* @return The text message body
*/
private static String buildMessageBodyFromPdus(SmsMessage[] msgs) {
if (msgs.length == 1) {
// There is only one part, so grab the body directly.
return replaceFormFeeds(msgs[0].getDisplayMessageBody());
} else {
// Build up the body from the parts.
StringBuilder body = new StringBuilder();
for (SmsMessage msg : msgs) {
// getDisplayMessageBody() can NPE if mWrappedMessage inside is null.
body.append(msg.getDisplayMessageBody());
}
return replaceFormFeeds(body.toString());
}
}
// Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
private static String replaceFormFeeds(String s) {
return s == null ? "" : s.replace('\f', '\n');
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SMS_RECEIVED)) {
// TODO: Try to find the receiver number to be able to populate
// SmsMessage.receiver.
// TODO: Get the id and the date from the stock app saved message.
// Using the stock app saved message require us to wait for it to
// be saved which can lead to race conditions.
Bundle bundle = intent.getExtras();
if (bundle == null) {
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_DELIVER_ACTION) ||
intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
// If we're the default SMS, ignore SMS_RECEIVED intents since we'll handle
// the SMS_DELIVER intent instead.
if (isDefaultSmsApp(GeckoAppShell.getContext()) && intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
return;
}
Object[] pdus = (Object[]) bundle.get("pdus");
for (int i=0; i<pdus.length; ++i) {
SmsMessage msg = SmsMessage.createFromPdu((byte[])pdus[i]);
notifySmsReceived(msg.getDisplayOriginatingAddress(),
msg.getDisplayMessageBody(),
getGeckoMessageClass(msg.getMessageClass()),
System.currentTimeMillis());
// TODO: Try to find the receiver number to be able to populate
// SmsMessage.receiver.
SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
if (messages == null || messages.length == 0) {
return;
}
SmsMessage sms = messages[0];
String body = buildMessageBodyFromPdus(messages);
long timestamp = System.currentTimeMillis();
int id = 0;
// We only need to save the message if we're the default SMS app
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_DELIVER_ACTION)) {
id = saveReceivedMessage(context,
sms.getDisplayOriginatingAddress(),
body,
sms.getTimestampMillis(),
timestamp,
sms.getPseudoSubject());
}
notifySmsReceived(id,
sms.getDisplayOriginatingAddress(),
body,
getGeckoMessageClass(sms.getMessageClass()),
sms.getTimestampMillis(),
timestamp);
return;
}
@ -447,7 +484,9 @@ public class GeckoSmsManager
if (envelope.isFailing(part)) {
if (part == Envelope.SubParts.SENT_PART) {
notifySmsSendFailed(envelope.getError(), bundle.getInt("requestId"));
if (bundle.getBoolean("shouldNotify")) {
notifySmsSendFailed(envelope.getError(), bundle.getInt("requestId"));
}
Log.i("GeckoSmsManager", "SMS sending failed!");
} else {
notifySmsDelivery(envelope.getMessageId(),
@ -463,16 +502,24 @@ public class GeckoSmsManager
String message = bundle.getString("message");
long timestamp = System.currentTimeMillis();
int id = saveSentMessage(number, message, timestamp);
// save message only if we're default SMS app, otherwise sendTextMessage does it for us
int id = 0;
if (isDefaultSmsApp(GeckoAppShell.getContext())) {
id = saveSentMessage(number, message, timestamp);
}
notifySmsSent(id, number, message, timestamp,
bundle.getInt("requestId"));
if (bundle.getBoolean("shouldNotify")) {
notifySmsSent(id, number, message, timestamp, bundle.getInt("requestId"));
}
envelope.setMessageId(id);
envelope.setMessageTimestamp(timestamp);
Log.i("GeckoSmsManager", "SMS sending was successful!");
} else {
Uri id = ContentUris.withAppendedId(kSmsContentUri, envelope.getMessageId());
updateMessageStatus(id, Telephony.Sms.STATUS_COMPLETE);
notifySmsDelivery(envelope.getMessageId(),
kDeliveryStatusSuccess,
bundle.getString("number"),
@ -491,19 +538,22 @@ public class GeckoSmsManager
}
@Override
public void send(String aNumber, String aMessage, int aRequestId) {
public void send(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify) {
int envelopeId = Postman.kUnknownEnvelopeId;
try {
SmsManager sm = SmsManager.getDefault();
Intent sentIntent = new Intent(ACTION_SMS_SENT);
Intent deliveredIntent = new Intent(ACTION_SMS_DELIVERED);
Intent sentIntent = new Intent(GeckoAppShell.getContext(), GeckoSmsManager.class);
sentIntent.setAction(ACTION_SMS_SENT);
Intent deliveredIntent = new Intent(GeckoAppShell.getContext(), GeckoSmsManager.class);
deliveredIntent.setAction(ACTION_SMS_DELIVERED);
Bundle bundle = new Bundle();
bundle.putString("number", aNumber);
bundle.putString("message", aMessage);
bundle.putInt("requestId", aRequestId);
bundle.putBoolean("shouldNotify", aShouldNotify);
if (aMessage.length() <= kMaxMessageSize) {
envelopeId = Postman.getInstance().createEnvelope(1);
@ -533,7 +583,7 @@ public class GeckoSmsManager
pendingIntentGuid.incrementAndGet(), deliveredIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
sm.sendTextMessage(aNumber, "", aMessage,
sm.sendTextMessage(aNumber, null, aMessage,
sentPendingIntent, deliveredPendingIntent);
} else {
ArrayList<String> parts = sm.divideMessage(aMessage);
@ -562,7 +612,7 @@ public class GeckoSmsManager
);
}
sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents,
sm.sendMultipartTextMessage(aNumber, null, parts, sentPendingIntents,
deliveredPendingIntents);
}
} catch (Exception e) {
@ -579,11 +629,13 @@ public class GeckoSmsManager
public int saveSentMessage(String aRecipient, String aBody, long aDate) {
try {
ContentValues values = new ContentValues();
values.put("address", aRecipient);
values.put("body", aBody);
values.put("date", aDate);
values.put(Telephony.Sms.ADDRESS, aRecipient);
values.put(Telephony.Sms.BODY, aBody);
values.put(Telephony.Sms.DATE, aDate);
// Always 'PENDING' because we always request status report.
values.put("status", kInternalDeliveryStatusPending);
values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_PENDING);
values.put(Telephony.Sms.SEEN, 0);
values.put(Telephony.Sms.READ, 0);
ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
Uri uri = cr.insert(kSmsSentContentUri, values);
@ -606,6 +658,41 @@ public class GeckoSmsManager
}
}
public void updateMessageStatus(Uri aId, int aStatus) {
ContentValues values = new ContentValues(1);
values.put(Telephony.Sms.STATUS, aStatus);
ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
if (cr.update(aId, values, null, null) != 1) {
Log.i("GeckoSmsManager", "Failed to update message status!");
}
}
public int saveReceivedMessage(Context aContext, String aSender, String aBody, long aDateSent, long aDate, String aSubject) {
ContentValues values = new ContentValues();
values.put(Telephony.Sms.Inbox.ADDRESS, aSender);
values.put(Telephony.Sms.Inbox.BODY, aBody);
values.put(Telephony.Sms.Inbox.DATE_SENT, aDateSent);
values.put(Telephony.Sms.Inbox.DATE, aDate);
values.put(Telephony.Sms.Inbox.STATUS, Telephony.Sms.STATUS_COMPLETE);
values.put(Telephony.Sms.Inbox.READ, 0);
values.put(Telephony.Sms.Inbox.SEEN, 0);
ContentResolver cr = aContext.getContentResolver();
Uri uri = cr.insert(kSmsContentUri, values);
long id = ContentUris.parseId(uri);
// The DOM API takes a 32bits unsigned int for the id. It's unlikely that
// we happen to need more than that but it doesn't cost to check.
if (id > UNSIGNED_INTEGER_MAX_VALUE) {
Log.i("GeckoSmsManager", "The id we received is higher than the higher allowed value.");
return -1;
}
return (int)id;
}
@Override
public void getMessage(int aMessageId, int aRequestId) {
class GetMessageRunnable implements Runnable {
@ -1079,13 +1166,13 @@ public class GeckoSmsManager
}
private int getGeckoDeliveryStatus(int aDeliveryStatus) {
if (aDeliveryStatus == kInternalDeliveryStatusNone) {
if (aDeliveryStatus == Telephony.Sms.STATUS_NONE) {
return kDeliveryStatusNotApplicable;
}
if (aDeliveryStatus >= kInternalDeliveryStatusFailed) {
if (aDeliveryStatus >= Telephony.Sms.STATUS_FAILED) {
return kDeliveryStatusError;
}
if (aDeliveryStatus >= kInternalDeliveryStatusPending) {
if (aDeliveryStatus >= Telephony.Sms.STATUS_PENDING) {
return kDeliveryStatusPending;
}
return kDeliveryStatusSuccess;
@ -1127,7 +1214,7 @@ public class GeckoSmsManager
}
@WrapForJNI
private static native void notifySmsReceived(String aSender, String aBody, int aMessageClass, long aTimestamp);
public static native void notifySmsReceived(int aId, String aSender, String aBody, int aMessageClass, long aSentTimestamp, long aTimestamp);
@WrapForJNI
private static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp, int aRequestId);
@WrapForJNI

View File

@ -28,7 +28,7 @@ public class SmsManager {
void stop();
void shutdown();
void send(String aNumber, String aMessage, int aRequestId);
void send(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify);
void getMessage(int aMessageId, int aRequestId);
void deleteMessage(int aMessageId, int aRequestId);
void markMessageRead(int aMessageId, boolean aValue, boolean aSendReadReport, int aRequestId);

View File

@ -362,16 +362,16 @@ Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv * arg0, jclass ar
#ifdef JNI_STUBS
typedef void (*Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t)(JNIEnv *, jclass, jstring, jstring, jint, jlong);
typedef void (*Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t)(JNIEnv *, jclass, jint, jstring, jstring, jint, jlong, jlong);
static Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived;
extern "C" NS_EXPORT void MOZ_JNICALL
Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv * arg0, jclass arg1, jstring arg2, jstring arg3, jint arg4, jlong arg5) {
Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv * arg0, jclass arg1, jint arg2, jstring arg3, jstring arg4, jint arg5, jlong arg6, jlong arg7) {
if (!f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived) {
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
"JNI Function called before it was loaded");
return ;
}
f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(arg0, arg1, arg2, arg3, arg4, arg5);
f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
#endif

View File

@ -2391,13 +2391,15 @@ public:
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
int32_t,
mozilla::jni::String::Param,
mozilla::jni::String::Param,
int32_t,
int64_t,
int64_t> Args;
static constexpr char name[] = "notifySmsReceived";
static constexpr char signature[] =
"(Ljava/lang/String;Ljava/lang/String;IJ)V";
"(ILjava/lang/String;Ljava/lang/String;IJJ)V";
static const bool isStatic = true;
static const bool isMultithreaded = false;
static const mozilla::jni::ExceptionMode exceptionMode =