Bug 674725 - Part U - Implement WebSMS delivered event for the Android backend. r=cjones

This commit is contained in:
Mounir Lamouri 2012-01-13 14:26:24 +01:00
parent ad59da3942
commit 7a1bfa48dc
6 changed files with 244 additions and 71 deletions

View File

@ -417,6 +417,7 @@ abstract public class GeckoApp
IntentFilter smsFilter = new IntentFilter();
smsFilter.addAction(GeckoSmsManager.ACTION_SMS_RECEIVED);
smsFilter.addAction(GeckoSmsManager.ACTION_SMS_SENT);
smsFilter.addAction(GeckoSmsManager.ACTION_SMS_DELIVERED);
mSmsReceiver = new GeckoSmsManager();
registerReceiver(mSmsReceiver, smsFilter);

View File

@ -122,7 +122,9 @@ public class GeckoAppShell
public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
public static native void notifySmsReceived(String aSender, String aBody, long aTimestamp);
public static native void onSmsSent(String aReceiver, String aBody, long aTimestamp);
public static native int saveMessageInSentbox(String aReceiver, String aBody, long aTimestamp);
public static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp);
public static native void notifySmsDelivered(int aId, String aReceiver, String aBody, long aTimestamp);
// A looper thread, accessed by GeckoAppShell.getHandler
private static class LooperThread extends Thread {

View File

@ -77,30 +77,77 @@ class PendingIntentUID
*/
class Envelope
{
protected int mId;
protected int mRemainingParts;
protected boolean mFailing;
enum SubParts {
SENT_PART,
DELIVERED_PART
}
protected int mId;
protected int mMessageId;
protected long mMessageTimestamp;
/**
* Number of sent/delivered remaining parts.
* @note The array has much slots as SubParts items.
*/
protected int[] mRemainingParts;
/**
* Whether sending/delivering is currently failing.
* @note The array has much slots as SubParts items.
*/
protected boolean[] mFailing;
public Envelope(int aId, int aParts) {
mId = aId;
mRemainingParts = aParts;
mFailing = false;
mMessageId = -1;
mMessageTimestamp = 0;
int size = Envelope.SubParts.values().length;
mRemainingParts = new int[size];
mFailing = new boolean[size];
for (int i=0; i<size; ++i) {
mRemainingParts[i] = aParts;
mFailing[i] = false;
}
}
public void decreaseRemainingParts() {
--mRemainingParts;
public void decreaseRemainingParts(Envelope.SubParts aType) {
--mRemainingParts[aType.ordinal()];
if (mRemainingParts[SubParts.SENT_PART.ordinal()] >
mRemainingParts[SubParts.DELIVERED_PART.ordinal()]) {
Log.e("GeckoSmsManager", "Delivered more parts than we sent!?");
}
}
public boolean arePartsRemaining() {
return mRemainingParts != 0;
public boolean arePartsRemaining(Envelope.SubParts aType) {
return mRemainingParts[aType.ordinal()] != 0;
}
public void markAsFailed() {
mFailing = true;
public void markAsFailed(Envelope.SubParts aType) {
mFailing[aType.ordinal()] = true;
}
public boolean isFailing() {
return mFailing;
public boolean isFailing(Envelope.SubParts aType) {
return mFailing[aType.ordinal()];
}
public int getMessageId() {
return mMessageId;
}
public void setMessageId(int aMessageId) {
mMessageId = aMessageId;
}
public long getMessageTimestamp() {
return mMessageTimestamp;
}
public void setMessageTimestamp(long aMessageTimestamp) {
mMessageTimestamp = aMessageTimestamp;
}
}
@ -168,8 +215,10 @@ class Postman
public class GeckoSmsManager
extends BroadcastReceiver
{
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_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";
private final static int kMaxMessageSize = 160;
@Override
@ -200,7 +249,8 @@ public class GeckoSmsManager
return;
}
if (intent.getAction().equals(ACTION_SMS_SENT)) {
if (intent.getAction().equals(ACTION_SMS_SENT) ||
intent.getAction().equals(ACTION_SMS_DELIVERED)) {
Bundle bundle = intent.getExtras();
if (bundle == null || !bundle.containsKey("envelopeId") ||
@ -211,31 +261,66 @@ public class GeckoSmsManager
int envelopeId = bundle.getInt("envelopeId");
Postman postman = Postman.getInstance();
Envelope envelope = postman.getEnvelope(envelopeId);
envelope.decreaseRemainingParts();
Envelope envelope = postman.getEnvelope(envelopeId);
if (envelope == null) {
Log.e("GeckoSmsManager", "Got an invalid envelope id (or Envelope has been destroyed)!");
return;
}
Envelope.SubParts part = intent.getAction().equals(ACTION_SMS_SENT)
? Envelope.SubParts.SENT_PART
: Envelope.SubParts.DELIVERED_PART;
envelope.decreaseRemainingParts(part);
if (getResultCode() != Activity.RESULT_OK) {
// TODO: manage error types.
Log.i("GeckoSmsManager", "SMS part sending failed!");
envelope.markAsFailed();
envelope.markAsFailed(part);
}
if (envelope.arePartsRemaining()) {
if (envelope.arePartsRemaining(part)) {
return;
}
if (envelope.isFailing()) {
// TODO: inform about the send failure.
Log.i("GeckoSmsManager", "SMS sending failed!");
if (envelope.isFailing(part)) {
if (part == Envelope.SubParts.SENT_PART) {
// TODO: inform about the send failure.
Log.i("GeckoSmsManager", "SMS sending failed!");
} else {
// It seems unlikely to get a result code for a failure to deliver.
// Even if, we don't want to do anything with this.
Log.e("GeckoSmsManager", "SMS failed to be delivered... is that even possible?");
}
} else {
GeckoAppShell.onSmsSent(bundle.getString("number"),
bundle.getString("message"),
System.currentTimeMillis());
Log.i("GeckoSmsManager", "SMS sending was successfull!");
if (part == Envelope.SubParts.SENT_PART) {
String number = bundle.getString("number");
String message = bundle.getString("message");
long timestamp = System.currentTimeMillis();
int id = GeckoAppShell.saveMessageInSentbox(number, message, timestamp);
GeckoAppShell.notifySmsSent(id, number, message, timestamp);
envelope.setMessageId(id);
envelope.setMessageTimestamp(timestamp);
Log.i("GeckoSmsManager", "SMS sending was successfull!");
} else {
GeckoAppShell.notifySmsDelivered(envelope.getMessageId(),
bundle.getString("number"),
bundle.getString("message"),
envelope.getMessageTimestamp());
Log.i("GeckoSmsManager", "SMS succesfully delivered!");
}
}
postman.destroyEnvelope(envelopeId);
// Destroy the envelope object only if the SMS has been sent and delivered.
if (!envelope.arePartsRemaining(Envelope.SubParts.SENT_PART) &&
!envelope.arePartsRemaining(Envelope.SubParts.DELIVERED_PART)) {
postman.destroyEnvelope(envelopeId);
}
return;
}
@ -257,15 +342,18 @@ public class GeckoSmsManager
SmsManager sm = SmsManager.getDefault();
Intent sentIntent = new Intent(ACTION_SMS_SENT);
Intent deliveredIntent = new Intent(ACTION_SMS_DELIVERED);
Bundle bundle = new Bundle();
bundle.putString("number", aNumber);
bundle.putString("message", aMessage);
if (aMessage.length() <= kMaxMessageSize) {
envelopeId = Postman.getInstance().createEnvelope(1);
bundle.putInt("envelopeId", envelopeId);
sentIntent.putExtras(bundle);
deliveredIntent.putExtras(bundle);
/*
* There are a few things to know about getBroadcast and pending intents:
@ -283,16 +371,25 @@ public class GeckoSmsManager
PendingIntentUID.generate(), sentIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
sm.sendTextMessage(aNumber, "", aMessage, sentPendingIntent, null);
PendingIntent deliveredPendingIntent =
PendingIntent.getBroadcast(GeckoApp.surfaceView.getContext(),
PendingIntentUID.generate(), deliveredIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
sm.sendTextMessage(aNumber, "", aMessage,
sentPendingIntent, deliveredPendingIntent);
} else {
ArrayList<String> parts = sm.divideMessage(aMessage);
envelopeId = Postman.getInstance().createEnvelope(parts.size());
bundle.putInt("envelopeId", envelopeId);
sentIntent.putExtras(bundle);
deliveredIntent.putExtras(bundle);
ArrayList<PendingIntent> sentPendingIntents =
new ArrayList<PendingIntent>(parts.size());
ArrayList<PendingIntent> deliveredPendingIntents =
new ArrayList<PendingIntent>(parts.size());
for (int i=0; i<parts.size(); ++i) {
sentPendingIntents.add(
@ -300,9 +397,16 @@ public class GeckoSmsManager
PendingIntentUID.generate(), sentIntent,
PendingIntent.FLAG_CANCEL_CURRENT)
);
deliveredPendingIntents.add(
PendingIntent.getBroadcast(GeckoApp.surfaceView.getContext(),
PendingIntentUID.generate(), deliveredIntent,
PendingIntent.FLAG_CANCEL_CURRENT)
);
}
sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents, null);
sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents,
deliveredPendingIntents);
}
} catch (Exception e) {
Log.e("GeckoSmsManager", "Failed to send an SMS: ", e);

View File

@ -180,6 +180,24 @@ Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one
return f_ ## name(jenv, jc, one, two, three); \
}
#define SHELL_WRAPPER4(name,type1,type2,type3,type4) \
typedef void (*name ## _t)(JNIEnv *, jclass, type1 one, type2 two, type3 three, type4 four); \
static name ## _t f_ ## name; \
extern "C" NS_EXPORT void JNICALL \
Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one, type2 two, type3 three, type4 four) \
{ \
f_ ## name(jenv, jc, one, two, three, four); \
}
#define SHELL_WRAPPER4_WITH_RETURN(name, return_type, type1, type2, type3, type4) \
typedef return_type (*name ## _t)(JNIEnv *, jclass, type1 one, type2 two, type3 three, type4 four); \
static name ## _t f_ ## name; \
extern "C" NS_EXPORT return_type JNICALL \
Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one, type2 two, type3 three, type4 four) \
{ \
return f_ ## name(jenv, jc, one, two, three, four); \
}
SHELL_WRAPPER0(nativeInit)
SHELL_WRAPPER1(nativeRun, jstring)
SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
@ -198,7 +216,9 @@ SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
SHELL_WRAPPER0(bindWidgetTexture);
SHELL_WRAPPER0_WITH_RETURN(testDirectTexture, bool);
SHELL_WRAPPER3(onSmsSent, jstring, jstring, jlong);
SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong);
SHELL_WRAPPER4(notifySmsSent, jint, jstring, jstring, jlong);
SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong);
static void * xul_handle = NULL;
static time_t apk_mtime = 0;
@ -586,7 +606,9 @@ loadLibs(const char *apkName)
GETFUNC(notifySmsReceived);
GETFUNC(bindWidgetTexture);
GETFUNC(testDirectTexture);
GETFUNC(onSmsSent);
GETFUNC(saveMessageInSentbox);
GETFUNC(notifySmsSent);
GETFUNC(notifySmsDelivered);
#undef GETFUNC
sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
gettimeofday(&t1, 0);

View File

@ -1360,7 +1360,7 @@ AndroidBridge::SaveSentMessage(const nsAString& aRecipient,
{
ALOG_BRIDGE("AndroidBridge::SaveSentMessage");
AutoLocalJNIFrame jniFrame;
AutoLocalJNIFrame jniFrame(GetJNIForThread());
jstring jRecipient = GetJNIForThread()->NewString(PromiseFlatString(aRecipient).get(), aRecipient.Length());
jstring jBody = GetJNIForThread()->NewString(PromiseFlatString(aBody).get(), aBody.Length());
return GetJNIForThread()->CallStaticIntMethod(mGeckoAppShellClass, jSaveSentMessage, jRecipient, jBody, aDate);

View File

@ -86,7 +86,9 @@ extern "C" {
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited(JNIEnv *, jclass, jstring uri);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble, jboolean, jdouble);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsReceived(JNIEnv* jenv, jclass, jstring, jstring, jlong);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onSmsSent(JNIEnv* jenv, jclass, jstring, jstring, jlong);
NS_EXPORT PRInt32 JNICALL Java_org_mozilla_gecko_GeckoAppShell_saveMessageInSentbox(JNIEnv* jenv, jclass, jstring, jstring, jlong);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsSent(JNIEnv* jenv, jclass, jint, jstring, jstring, jlong);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsDelivered(JNIEnv* jenv, jclass, jint, jstring, jstring, jlong);
#ifdef MOZ_JAVA_COMPOSITOR
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass);
@ -275,56 +277,98 @@ Java_org_mozilla_gecko_GeckoAppShell_notifySmsReceived(JNIEnv* jenv, jclass,
NS_DispatchToMainThread(runnable);
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_onSmsSent(JNIEnv* jenv, jclass,
jstring aReceiver,
jstring aBody,
jlong aTimestamp)
NS_EXPORT PRInt32 JNICALL
Java_org_mozilla_gecko_GeckoAppShell_saveMessageInSentbox(JNIEnv* jenv, jclass,
jstring aReceiver,
jstring aBody,
jlong aTimestamp)
{
class OnSmsSentRunnable : public nsRunnable {
nsCOMPtr<nsISmsDatabaseService> smsDBService =
do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
if (!smsDBService) {
NS_ERROR("Sms Database Service not available!");
return -1;
}
PRInt32 id;
smsDBService->SaveSentMessage(nsJNIString(aReceiver, jenv),
nsJNIString(aBody, jenv), aTimestamp, &id);
return id;
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_notifySmsSent(JNIEnv* jenv, jclass,
jint aId,
jstring aReceiver,
jstring aBody,
jlong aTimestamp)
{
class NotifySmsSentRunnable : public nsRunnable {
public:
OnSmsSentRunnable(const nsAString& aReceiver, const nsAString& aBody, PRUint64 aTimestamp)
: mReceiver(aReceiver)
, mBody(aBody)
, mTimestamp(aTimestamp)
NotifySmsSentRunnable(const SmsMessageData& aMessageData)
: mMessageData(aMessageData)
{}
NS_IMETHODIMP Run() {
nsCOMPtr<nsISmsDatabaseService> smsDBService =
do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
if (!smsDBService) {
NS_ERROR("Sms Database Service not available!");
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return NS_OK;
}
int id;
smsDBService->SaveSentMessage(mReceiver, mBody, mTimestamp, &id);
nsCOMPtr<SmsMessage> message =
new SmsMessage(id, eDeliveryState_Sent, EmptyString(),
mReceiver, mBody, mTimestamp);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
NS_ERROR("Observer Service not available!");
return NS_OK;
}
nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
obs->NotifyObservers(message, kSmsSentObserverTopic, nsnull);
return NS_OK;
}
private:
nsString mReceiver;
nsString mBody;
PRUint64 mTimestamp;
SmsMessageData mMessageData;
};
nsCOMPtr<nsIRunnable> runnable =
new OnSmsSentRunnable(nsJNIString(aReceiver, jenv),
nsJNIString(aBody, jenv), aTimestamp);
SmsMessageData message(aId, eDeliveryState_Sent, EmptyString(),
nsJNIString(aReceiver, jenv),
nsJNIString(aBody, jenv), aTimestamp);
nsCOMPtr<nsIRunnable> runnable = new NotifySmsSentRunnable(message);
NS_DispatchToMainThread(runnable);
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_notifySmsDelivered(JNIEnv* jenv, jclass,
jint aId,
jstring aReceiver,
jstring aBody,
jlong aTimestamp)
{
class NotifySmsDeliveredRunnable : public nsRunnable {
public:
NotifySmsDeliveredRunnable(const SmsMessageData& aMessageData)
: mMessageData(aMessageData)
{}
NS_IMETHODIMP Run() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs) {
return NS_OK;
}
nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
obs->NotifyObservers(message, kSmsDeliveredObserverTopic, nsnull);
return NS_OK;
}
private:
SmsMessageData mMessageData;
};
SmsMessageData message(aId, eDeliveryState_Sent, EmptyString(),
nsJNIString(aReceiver, jenv),
nsJNIString(aBody, jenv), aTimestamp);
nsCOMPtr<nsIRunnable> runnable = new NotifySmsDeliveredRunnable(message);
NS_DispatchToMainThread(runnable);
}