Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
432 views
in Technique[技术] by (71.8m points)

android - Unable to send MMS using SmsManager

I am trying to make an app that would send a MMS without using the native Android messaging app. I followed the example here. My log statements seem to be correctly printing, but I can't figure out why the MMS is not being sent.

Also on a different note, I am a bit confused about where in the example the attachment (like an image) is being selected to send as MMS. I tried to import the demo into Android Studio but I ran into issues.

My function for sending MMS is below:

 public void sendMMS() {
    Log.d(TAG, "sendMMS()");
    Random random = new Random();
    final String fileName = "send." + String.valueOf(Math.abs(random.nextLong())) + ".dat";

    final File mSendFile = new File(mContext.getCacheDir(), fileName);
    // Making RPC call in non-UI thread
    AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
        @Override
        public void run() {
            final byte[] pdu = buildPdu();
            Uri writerUri = (new Uri.Builder())
                    .authority("com.example.appname")
                    .path(fileName)
                    .scheme(ContentResolver.SCHEME_CONTENT)
                    .build();
            Log.d(TAG, "sendMMS(): Uri: " + writerUri.toString());
            FileOutputStream writer = null;
            Uri contentUri = null;
            try {
                writer = new FileOutputStream(mSendFile);
                writer.write(pdu);
                contentUri = writerUri;
                Log.d(TAG, "sendMMS(): just wrote file");
            } catch (final IOException e) {
                Log.d(TAG, "sendMMS(): FAILED: couldn't write file");
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                    }
                }
            }
            if (contentUri != null) {
                SmsManager.getDefault().sendMultimediaMessage(mContext, contentUri, null, null, null);
                Log.d(TAG, "sendMMS(): just sent");
            } else {
                Log.d(TAG, "sendMMS(): FAILED: couldn't write file so didn't send");
            }
        }
    });

}

Helper functions

    private byte[] buildPdu() {
    final SendReq req = new SendReq();
    // from
    final String lineNumber = getSimNumber();
    if (!TextUtils.isEmpty(lineNumber)) {
        req.setFrom(new EncodedStringValue(lineNumber));
    }
    // to
    String[] destsArray = mDestList.toArray(new String[mDestList.size()]);
    EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(destsArray);
    if (encodedNumbers != null) {
        req.setTo(encodedNumbers);
    }
    // date
    req.setDate(System.currentTimeMillis() / 1000);
    // body
    PduBody body = new PduBody();
    // message text
    final int size = addMessagePart(body, true/* add text smil */);
    req.setBody(body);
    // message size
    req.setMessageSize(size);
    // message class
    req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
    // expiry
    req.setExpiry(DEFAULT_EXPIRY_TIME);
    try {
        // priority
        req.setPriority(DEFAULT_PRIORITY);
        // delivery report
        req.setDeliveryReport(PduHeaders.VALUE_NO);
        // read report
        req.setReadReport(PduHeaders.VALUE_NO);
    } catch (InvalidHeaderValueException e) {}
    return new PduComposer(mContext, req).make();
}

private String getSimNumber() {
    TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    return telephonyManager.getLine1Number();
}

private int addMessagePart(PduBody pb, boolean addTextSmil) {
    PduPart part = new PduPart();
    part.setCharset(CharacterSets.UTF_8);
    part.setContentType(ContentType.TEXT_PLAIN.getBytes());
    part.setContentLocation(TEXT_PART_FILENAME.getBytes());
    int index = TEXT_PART_FILENAME.lastIndexOf(".");
    String contentId = (index == -1) ? TEXT_PART_FILENAME : TEXT_PART_FILENAME.substring(0, index);
    part.setContentId(contentId.getBytes());
    part.setData(mMessage.getBytes());
    pb.addPart(part);
    if (addTextSmil) {
        String smil = String.format(sSmilText, TEXT_PART_FILENAME);
        addSmilPart(pb, smil);
    }
    return part.getData().length;

}

private void addSmilPart(PduBody pb, String smil) {
    PduPart smilPart = new PduPart();
    smilPart.setContentId("smil".getBytes());
    smilPart.setContentType(ContentType.APP_SMIL.getBytes());
    smilPart.setContentLocation("smil.xml".getBytes());
    smilPart.setData(smil.getBytes());
    pb.addPart(0, smilPart);
}

Relevant parts of my manifest

<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

Relevant instance variables

    private final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
private final String TEXT_PART_FILENAME = "text_0.txt";
private final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
private String mMessage;
private ArrayList<String> mDestList;
private Context mContext;
private static final String sSmilText =
        "<smil>" +
                "<head>" +
                    "<layout>" +
                        "<root-layout/>" +
                        "<region height="100%%" id="Text" left="0%%" top="0%%" width="100%%"/>" +
                    "</layout>" +
                "</head>" +
                "<body>" +
                    "<par dur="8000ms">" +
                        "<text src="%s" region="Text"/>" +
                    "</par>" +
                "</body>" +
        "</smil>";

I already do input checks, so by the time sendMMS() is called, my message and destList are not null.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The flow should be as such:

  1. Create the Mms send-request - new SendReq() and config its date, body, to, etc.
  2. Create the Mms body - new PduBody().
  3. Create Parts via new PduPart() for each attachment, and add to the body: body.addPart(pdu)
  4. Add the body to the request - req.setBody(body)
  5. Convert the send-request to a byte[] ready to be sent by calling new PduComposer(context, mySendReq).make() - note that you'll need to copy lots of code from Android's source code to get the PduComposer class.
  6. Now's the interesting part - you save the byte[] to a local file accessible to your app only, and add ContentProvider class that allows other apps to request access to your file, this is MmsFileProvider class in the sample app, don't forget to declare your provider in your manifest file.
  7. Now, when you call the SmsManager.sendMultimediaMessage api, your file provider will wake up to serve the file containing the pdu bytes to the system SmsManager that will read it and send it on the wire.

Having that said, this API is only working for me on some devices (e.g. Nexuses), but not on some others (e.g. HTC One). See my SO question here: SmsManager MMS APIs on HTC/LG


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...