Android์์ SMS๋ฅผ ์์ ํ์ฌ ์๋ฒ๋ก ์ค๊ณํ๋ ์ฑ์ ๋ง๋ค์๋ค. SMS๋ ์ ๋์ํ๋๋ฐ, MMS ์ง์์ ์ถ๊ฐํ๋ฉด์ ์ฌ๊ฐํ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ๋ค.
Table of contents
Open Table of contents
์๋ชป๋ ์ ๊ทผ
SMS ์์ ์ฝ๋๊ฐ ์ ๋์ํ๋๊น, MMS๋ ๋น์ทํ๊ฒ ํ๋ฉด ๋๊ฒ ๋ค๊ณ ์๊ฐํ๋ค.
// SMS ์์ โ ์ ์ ๋์
if ("android.provider.Telephony.SMS_RECEIVED".equals(action)) {
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
for (Object pdu : pdus) {
SmsMessage msg = SmsMessage.createFromPdu((byte[]) pdu, format);
// msg.getOriginatingAddress(), msg.getMessageBody() ์ฌ์ฉ
}
}
// MMS ์์ โ ์๋ชป๋ ์ฝ๋
if ("android.provider.Telephony.MMS_RECEIVED".equals(action)) {
// SMS์ ๋์ผํ PDU ํ์ฑ ์๋
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
// pdus๊ฐ null์ด๊ฑฐ๋, ํ์ฑํ๋ฉด ๊นจ์ง ๋ฐ์ดํฐ๊ฐ ๋์ด
}
๋ฌธ์ 1: MMS_RECEIVED๋ ํ์ค intent๊ฐ ์๋๋ค
android.provider.Telephony.MMS_RECEIVED๋ ๊ณต์ Android API์ ์กด์ฌํ์ง ์๋๋ค. ์ผ๋ถ ๊ธฐ๊ธฐ์์ ๋ฐ์ํ ์ ์์ง๋ง, ๋ณด์ฅ๋์ง ์๋๋ค. ์ค์ ๋ก MMS๋ WAP Push ๋ฉ์์ง๋ก ์ ๋ฌ๋๋ค.
๋ฌธ์ 2: MMS๋ PDU ๊ตฌ์กฐ๊ฐ ๋ค๋ฅด๋ค
SMS์ MMS๋ ์์ ํ ๋ค๋ฅธ ํ๋กํ ์ฝ์ด๋ค.
| ํญ๋ชฉ | SMS | MMS |
|---|---|---|
| ์ ๋ฌ ๋ฐฉ์ | ์ง์ PDU | WAP Push ์๋ฆผ โ ๋ค์ด๋ก๋ |
| Intent | SMS_RECEIVED | WAP_PUSH_RECEIVED |
| ๋ฐ์ดํฐ | extras์ โpdusโ ๋ฐฐ์ด | ContentProvider์ ์ ์ฅ |
| ํ์ฑ | SmsMessage.createFromPdu() | ContentResolver ์ฟผ๋ฆฌ |
SMS์ PDU ํ์ฑ ๋ก์ง์ MMS์ ์ ์ฉํ๋ฉด, pdus๊ฐ null์ด๊ฑฐ๋ ์๋ชป๋ ๋ฐ์ดํธ๋ฅผ ํ์ฑํ์ฌ ๊นจ์ง ๋ฌธ์์ด์ด ์๋ฒ๋ก ์ ์ก๋๋ silent data corruption์ด ๋ฐ์ํ๋ค. ์๋ฌ๊ฐ ๋์ง ์๊ณ ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋๋ ๋ฐ๊ฒฌ์ด ๋ฆ์๋ค.
์ฌ๋ฐ๋ฅธ MMS ์์ ๋ฐฉ๋ฒ
Manifest ์ค์
<receiver
android:name=".MmsReceiveBroadcast"
android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
ํต์ฌ:
WAP_PUSH_RECEIVEDintent ์ฌ์ฉ (MMS_RECEIVED ์๋)mimeTypeํํฐ๋ก MMS๋ง ์ ๋ณBROADCAST_WAP_PUSH๊ถํ ํ์
BroadcastReceiver ๊ตฌํ
MMS๋ WAP Push ์์ ์์ ์ ์์ง ContentProvider์ ์ ์ฅ๋์ง ์์์ ์ ์๋ค. ์ฝ๊ฐ์ ๋๋ ์ด๊ฐ ํ์ํ๋ค.
public class MmsReceiveBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!"android.provider.Telephony.WAP_PUSH_RECEIVED".equals(intent.getAction())) return;
PendingResult pendingResult = goAsync();
executor.execute(() -> {
try {
// MMS๊ฐ ContentProvider์ ์ ์ฅ๋ ๋๊น์ง ๋๊ธฐ
Thread.sleep(3000);
// ์ต์ MMS ID ์กฐํ
String mmsId = getLatestMmsId(context);
if (mmsId == null) return;
// ๋ฐ์ ์ ์ถ์ถ
String sender = getMmsSender(context, mmsId);
// ๋ณธ๋ฌธ ์ถ์ถ
String body = getMmsBody(context, mmsId);
// ์๋ฒ๋ก ์ ์ก
RelayClient.send(sender, body);
} finally {
pendingResult.finish();
}
});
}
}
ContentResolver๋ก MMS ๋ฐ์ดํฐ ์ถ์ถ
// ์ต์ MMS ID ์กฐํ
private String getLatestMmsId(Context context) {
try (Cursor cursor = context.getContentResolver().query(
Uri.parse("content://mms"),
new String[]{"_id"},
null, null, "_id DESC LIMIT 1")) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0);
}
}
return null;
}
// ๋ฐ์ ์ ์ถ์ถ (addr ํ
์ด๋ธ, type=137=FROM)
private String getMmsSender(Context context, String mmsId) {
try (Cursor cursor = context.getContentResolver().query(
Uri.parse("content://mms/" + mmsId + "/addr"),
new String[]{"address"},
"type=137", null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0);
}
}
return null;
}
// ๋ณธ๋ฌธ ์ถ์ถ (part ํ
์ด๋ธ, text/plain)
private String getMmsBody(Context context, String mmsId) {
try (Cursor cursor = context.getContentResolver().query(
Uri.parse("content://mms/part"),
new String[]{"_id", "ct", "text", "_data"},
"mid=" + mmsId + " AND ct='text/plain'", null, null)) {
if (cursor != null && cursor.moveToFirst()) {
String text = cursor.getString(cursor.getColumnIndex("text"));
if (text != null) return text;
// text ํ๋๊ฐ null์ด๋ฉด _data ๊ฒฝ๋ก์์ ์ฝ๊ธฐ
String dataPath = cursor.getString(cursor.getColumnIndex("_data"));
if (dataPath != null) {
return readFromContentResolver(context, cursor.getString(0));
}
}
}
return null;
}
๋ฆฌ๋ค์ด๋ฐ ์ ์ฃผ์์ฌํญ
์ฑ ํจํค์ง๋ช ์ ๋ณ๊ฒฝํ ๋ HTTP ํค๋๋ ํจ๊ป ๋ณ๊ฒฝํด์ผ ํ๋ค. ์๋ฒ์์ ์ธ์ฆ ํค๋๋ก ์ฑ์ ์๋ณํ๋ ๊ฒฝ์ฐ, ํค๋๋ช ๋ณ๊ฒฝ์ด ์๋ฒ์ ๋๊ธฐํ๋์ง ์์ผ๋ฉด 401 ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
// Before
connection.setRequestProperty("X-MyApp-OldName-Key", apiKey);
// After โ ์๋ฒ ์ธก๋ ๋์์ ๋ณ๊ฒฝํด์ผ ํจ
connection.setRequestProperty("X-MyApp-NewName-Key", apiKey);
ํต์ฌ ์ ๋ฆฌ
- MMS๋ SMS์ ์์ ํ ๋ค๋ฅธ ํ๋กํ ์ฝ์ด๋ค โ PDU ํ์ฑ์ ๊ณต์ ํ ์ ์๋ค
- MMS_RECEIVED intent๋ ๋นํ์ค์ด๋ค โ
WAP_PUSH_RECEIVED๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค - MMS ๋ฐ์ดํฐ๋ ContentProvider์์ ์ฟผ๋ฆฌํ๋ค โ extras์์ ์ง์ ์ถ์ถํ ์ ์๋ค
- 3์ด ๋๋ ์ด๊ฐ ํ์ํ๋ค โ WAP Push ์์ ์์ ์ MMS๊ฐ ์์ง ์ ์ฅ๋์ง ์์์ ์ ์๋ค
- silent data corruption์ด ๊ฐ์ฅ ์ํํ๋ค โ ์๋ฌ ์์ด ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋๋ฉด ๋ฐ๊ฒฌ์ด ๋ฆ๋ค
์ฐธ๊ณ ์๋ฃ
- Android SmsMessage API โ Android ๊ณต์ SmsMessage ํด๋์ค ๋ฌธ์
- WapPushOverSms.java (AOSP) โ MMS PDU ๋ด๋ถ ์ฒ๋ฆฌ ์์ค