Tuesday, September 24, 2013

How to resolve the problem when Ubuntu on VMWare cannot get the ip address.

Ubuntu 3.8.0 64-bit installed on VMWare 9 could not get the ip address.

Network is configured as DHCP in Ubuntu and the bridge mode is set on VMWare VM configuration.
At first run, it worked and even samba service with Windows 7, the host OS worked successfully.

Today it does not work at all and no ip address is acquired when I checked with 'ifconfig' command.
Shutting down and restarting the VM was not the solution.

Googling told me the answer.

Set the /etc/network/interfaces file as follows.
============================
auto eth0
iface eth0 inet dhcp
============================

There was "lo" instead of "eth0".
Still I don't understand why it worked before and now I have to change the above file.

In some linux, the file is /etc/networking/interfaces, the google told.

Thanks, Google.

Sunday, September 22, 2013

ODEX decompilation

ODEX is an Optimized DEX, which means a .odex file is a runtime instance of the dalvik byte code binary .dex file.
Usually .dex file exists inside of .apk and it turns into .odex when running and stored in the dalvik cache. If the binary file exists in the form of .odex file, then it exists outside of .apk file and it is not stored in the dalvik cache. .odex file can boost the initial loading performance of android app.

To decompile .dex files
1. Convert .dex file into .jar file using dex2jar utility.
2. Look into .jar file using jd-gui
   or extract .jar file into .class files and convert .class files into .java files using jad utility.


To decompile .odex files, you need some prior steps changing .odex file into .dex file.

0. prerequisites
   - baksmali.jar & smali.jar from http://code.google.com/p/smali/downloads/list
   - system/framework folder and the files in it.(This is usually included in the system.img of rom images.)
   - Of course, the target .odex file.(for example "target_sample.odex")

 1. Convert .odex file into Smali format files using baksmali utility.
    java -jar baksmali.jar -d system/framework -x target_sample.odex
   -Here baksmali.jar filename may be changed depending on the file name and the version of baksmali.
   - "system/framework" folder may be different depending on the place where you put it.
   - Then the Smali formatted files are generated in the "out" folder.

 2. Bind the smali formatted files into .dex file.
    java -jar smali.jar -o classes.dex out
   -Here smali.jar filename may be changed depending on the file name and the version of smali.
   - "-o" option is for designating the output file name of .dex file, so you can change the classes.dex into anything you want.
   - the last "out" is the folder name generated in the prior step.


Now you have the .dex file and you can proceed the dex/java decompilation.




=======================================================================
Here I wrote a batch file script for the above steps.

Put a .odex file & framework folder and run the script.
Then the decompilation of .odex-->.dex-->.jar-->.java is done at one shot.

All the result and the related files are saved into a folder given as the argument of the script.
The argument should be the name of .odex file without the extension .odex.

The assumptions are as follows.
0. In Windows7 environment, jdk is installed.(Test with jdk 1.7.0_25)
1. dex2jar-0.0.9.13 folder exists. Of course dex2jar.bat should be there.
2. baksmali-2.0b6.jar exists.
3. jad.exe exists.
4. smali-2.0b6.jar exists.
5. Save the below script as odex2jard.bat.
(If some files have different version, then the script should be revised accordingly.)
===========================Start of script ================================
@echo off
if x%1x == xx (
echo ....................................
echo No argument.
echo ....................................
goto usage
)
if not exist .\%1.odex (
echo ....................................
echo %1.odex file does not exist.
echo ....................................
goto usage
)
if exist .\%1.dex (
echo ....................................
echo %1.dex file exists.
echo Move or remove existing %1.dex file.
echo ....................................
goto usage
)
if exist .\%1_dex2jar (
echo ....................................
echo %1_dex2jar folder exists.
echo Move or remove existing %1_dex2jar folder.
echo ....................................
goto usage
)
if exist .\%1_dex2jar.jar (
echo ....................................
echo %1_dex2jar.jar file exists.
echo Move or remove existing %1_dex2jar.jar file.
echo ....................................
goto usage
)
if exist .\%1 (
echo ....................................
echo The result is saved into %1 folder.
echo Move or remove existing %1 folder.
echo ....................................
goto usage
)
if exist .\out (
echo ....................................
echo "out" folder is used as temp.
echo Move or remove "out" folder.
echo ....................................
goto usage
)
if not exist .\framework (
echo ....................................
echo No "framework" folder!!
echo The framework folder should be right here.
echo Cautious not to be system/framework!!
echo ....................................
goto usage
)
java -jar baksmali-2.0b6.jar -d framework -x %1.odex
java -jar smali-2.0b6.jar -o %1.dex out
rmdir /S /Q .\out
call .\dex2jar-0.0.9.13\dex2jar.bat %1.dex
mkdir %1_dex2jar
cd %1_dex2jar
jar xvf ..\%1_dex2jar.jar
cd ..
.\jad -o -d %1_dex2jar -r -s java %1_dex2jar/**/*.class
mkdir %1
move .\framework %1
move .\%1.odex %1
move .\%1.dex %1
move .\%1_dex2jar.jar %1
move .\%1_dex2jar %1
echo ....................................
echo DONE !!
echo ....................................
goto :eof
:usage
echo .
echo Usage:
echo ....................................
echo odex2jard.bat "target_filename"
echo ....................................
echo No file extension at "target_filename"
echo eg) For abc.odex, type as follows
echo odex2jard.bat abc
echo ....................................
goto :eof

=========================== Endof script =================================

Most frequently used jad command options as a batch file.

jad and jd-gui are usually used to decompile java classes.
When using jad, all the class files in the directory structure need to be decomplied at once.
So I wrote a batch file working in the cmd window of Windows environment.

Copy the following lines and save them as jad.bat.
Run jad.bat with the only argument, the target directory name. All the class files should be unzipped under the target directory in advance.

@echo off
if x%1x == xx (
        echo .
        echo Usage:
        echo ....................................
        echo         jad.bat "target_dir"
        echo ....................................
        goto :eof
)
.\jad -o -d %1 -r -s java %1/**/*.class

You don't need to remember all the options of jad and don't need to revisit them every time you try.
You can write down the script for *nix family easily.

Thanks.

How to read .rfs files, a form of android rom images.

To inspect the .rfs files you only need a linux and the followung simple command.

# mount -o loop factory.rfs /target_dir

Here factory.rfs is the rom image file and the target_dir is the directory you want to mount the .rfs image.



I've tested this on my Ubuntu 3.8.0 64-bit running as a guest OS on VMWare 9.

This command line requires the root privilege.
So in fact, I have typed the following command.

# sudo mount -o loop factoryrfs.rfs ./rfs

The rom image file name was factoryrfs.rfs and the target directory was rfs under the current directory.

Saturday, September 21, 2013

How to enable Korean language input keyboard on Ubuntu

http://xmodulo.com/2012/11/how-to-enable-korean-language-input-on-ubuntu.html


How to enable Korean language input keyboard on Ubuntu

If you would like to use foreign language input on Ubuntu, there are two ways to enable foreign language input. One method is using Smart Common Input Method platform (SCIM), and the other one is to use Intelligent Input Bus (iBus).
SCIM is the original input method used to support dozens of different foreign languages in Ubuntu prior to Ubuntu version 9.10. However, SCIM is known to be somewhat unstable, may unduly eat up system resources, and could demand re-install from time to time.
iBus is a new input method platform designed to overcome some of the limitations of SCIM. Unlike SCIM, iBus has separate iBus daemon and client processes (e.g., input engines and configuration tools), and loads any necessary engine on demand. As such, iBus improves the modularity and stability of the input system, as well as save on startup time and memory footprint. iBus is the default input method framework in use for Ubuntu 10.04 (Lucid) and higher.
If you would like to enable Korean language input on Ubuntu, you can use either SCIM or iBus, depending on your Ubuntu version.

Enable Korean language input on Ubuntu 9.04 and lower

Go to “System Settings” -> “Keyboard”. Click on “Layout Settings”. Click on “+” at the bottom to add Korean keyboard layout.
Now, install scim as follows.
$ sudo apt-get install scim-hangul
Log out and log in again. Go to “System Settings” -> “Language Support”. Choose scim-bridge in Keyboard input method system. Finally reboot.
At this point, you can now switch between English and Korean by pressing Ctrl+Space.

Enable Korean language input on Ubuntu 9.10 and higher

Although you can still use SCIM on newer versions of Ubuntu, it is recommended that you use iBus for reasons mentioned above. Note that if you have installed SCIM on your system before, you need to completely remove all SCIM-related packages (e.g., search for “SCIM” using synaptic), before installing iBus. The following guide on using iBus has been tested on Ubuntu 12.10 Desktop.
To install iBus, go to “System Settings” -> “Language Support”. Click on “Install/Remove Language”. Choose “Korean” language, and install it.
After having installed Korean language, choose “ibus” for Keyboard input method system in the same “Language Support” window.
On Ubuntu Dashboard, search for “Keyboard input methods”, and launch it. Under “Input Method” tab, click on “Customize active input methods”, and add “Korean” to input method. Under “General” tab, you can configure keyboard shortcuts to toggle between English and Korean. The default keyboard shortcut is Ctrl+Space.
If you would like to configure Hangul keyboard (e.g., dubeolsik, sebeolsik, etc), search for “IBus Hangul Preferences” in Ubuntu dashboard, and launch it to do that.
At this point, you can now toggle between English and Korean by pressing Ctrl+Space.

Android audio articles & etc - http://shadowxx.egloos.com/category/%EC%BB%B4%EB%B6%80%EB%A6%AC%20%EC%9D%B4%EC%95%BC%EA%B8%B0

http://shadowxx.egloos.com/category/%EC%BB%B4%EB%B6%80%EB%A6%AC%20%EC%9D%B4%EC%95%BC%EA%B8%B0

though most of them are in Korean.

Compiling simg2img and make_ext4fs from Android v4.2.1 sources

http://muzso.hu/2012/08/10/how-to-pack-and-unpack-system.img-and-userdata.img-from-an-android-factory-image#comment-1678

gcc compiling option should be re-located, which is supposed to depends on the computer environment.


Compiling simg2img and make_ext4fs from Android v4.2.1 sources

The process is a bit different now.
Let's assume that the $AOSP_ROOT variable points to our AOSP root (eg. export AOSP_ROOT="$HOME/Android/AOSP").

  1. Download the https://android.googlesource.com/platform/system/core repository:
    cd "$AOSP_ROOT"
    [ ! -d "platform/system" ] && mkdir -p platform/system
    cd platform/system
    git clone https://android.googlesource.com/platform/system/core
  2. Check out a revision of your choice:
    cd core
    git checkout android-4.2.1_r1
  3. Compile simg2img:
    cd libsparse
    gcc -o simg2img -Iinclude -lz simg2img.c sparse_crc32.c backed_block.c output_file.c sparse.c sparse_err.c sparse_read.c

    gcc -o simg2img -Iinclude simg2img.c sparse_crc32.c backed_block.c output_file.c sparse.c sparse_err.c sparse_read.c -lz
    (When compiling, the link option should come at last on my computer. If not "undefined reference to xxxx" errors happened.)
The unpacking is quite the same:
  • Unpack your Android image files (of course you should adjust the commands for the correct path to the IMG files):
    cd ../../
    ./core/libsparse/simg2img system.img system.raw.img
    ./core/libsparse/simg2img userdata.img userdata.raw.img
  • Do whatever you want with the images (eg. you can use Paragon's ExtFS on a Mac or just simply mount the images in linux via the loop device). Eg.
    mkdir /mnt/my_system /mnt/my_userdata
    mount -t ext4 -o loop system.raw.img /mnt/my_system
    mount -t ext4 -o loop userdata.raw.img /mnt/my_userdata
And the compilation of make_ext4fs did get more complicated too:
  1. Download the https://android.googlesource.com/platform/system/extras repository:
    cd "$AOSP_ROOT/platform"
    [ ! -d "system" ] && mkdir system
    cd system
    git clone https://android.googlesource.com/platform/system/extras
  2. Check out a revision of your choice:
    cd extras
    git checkout android-4.2.1_r1
  3. Compile make_ext4fs:
    cd ..
    gcc -o make_ext4fs -Icore/libsparse/include -lz extras/ext4_utils/make_ext4fs_main.c extras/ext4_utils/make_ext4fs.c extras/ext4_utils/ext4fixup.c extras/ext4_utils/ext4_utils.c extras/ext4_utils/allocate.c extras/ext4_utils/contents.c extras/ext4_utils/extent.c extras/ext4_utils/indirect.c extras/ext4_utils/uuid.c extras/ext4_utils/sha1.c extras/ext4_utils/wipe.c core/libsparse/backed_block.c core/libsparse/output_file.c core/libsparse/sparse.c core/libsparse/sparse_crc32.c core/libsparse/sparse_err.c core/libsparse/sparse_read.c
And the repacking is all the same too:
PATH="$PATH:$(pwd)" ./extras/ext4_utils/mkuserimg.sh -s /mnt/my_system_dir my_system.img ext4 /tmp 512M

How To Install Git on Ubuntu 12.04

https://www.digitalocean.com/community/articles/how-to-install-git-on-ubuntu-12-04


How To Install Git on Ubuntu 12.04

What the Red Means

The lines that the user needs to enter or customize will be in red in this tutorial!

The rest should mostly be copy-and-pastable.

About Git


Git is a distributed version control system released to the public in 2005. The program allows for non-linear development of projects, and can handle large amounts of data effectively by storing it on the local server.

This tutorial will cover two ways to install Git.

How to Install Git with Apt-Get


Installing Git with apt-get is a quick and easy process. The program installs on the virtual private server with one command:
sudo apt-get install git-core

After it finishes downloading, you will have Git installed and ready to use.

How to Install Git from Source


If you are eager to download the most recent version of Git, it is generally a good idea to install it from the source.

Quickly run apt-get update to make sure that you download the most recent packages to your VPS.
sudo apt-get update

Prior to installing Git itself, download all of the required dependancies:
sudo apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential

Once they are installed, you can download the latest version of Git from the google code page.
wget https://git-core.googlecode.com/files/git-1.8.1.2.tar.gz

After it downloads, untar the file and switch into that directory:
tar -zxf git-1.8.1.2.tar.gz
cd git-1.8.1.2

If you want to do a global install, install it once as yourself and once as root, using the sudo prefix:
make prefix=/usr/local all
sudo make prefix=/usr/local install

If you need to update Git in the future, you can use Git itself to do it.
git clone git://git.kernel.org/pub/scm/git/git.git

How to Setup Git


After Git is installed, whether from apt-get or from the source, you need to copy your username and email in the gitconfig file. You can access this file at ~/.gitconfig.

Opening it following a fresh Git install would reveal a completely blank page:
sudo nano ~/.gitconfig

You can use the follow commands to add in the required information.
git config --global user.name "NewUser"
git config --global user.email newuser@example.com

You can see all of your settings with this command:
git config --list

If you avoid putting in your username and email, git will later attempt to fill it in for you, and you may end up with a message like this:
[master 0d9d21d] initial project version
 Committer: root 
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:

    git config --global user.name "Your Name"
    git config --global user.email you@example.com

After doing this, you may fix the identity used for this commit with:

    git commit --amend --reset-author

See More


This tutorial covered how to install Git on your virtual private server. Stay tuned for a second tutorial on Git Basics.



By Etel Sverdlov

Wednesday, September 18, 2013

How to Read MMS Data in Android?

http://stackoverflow.com/questions/3012287/how-to-read-mms-data-in-android

How to Read MMS Data in Android?

I want to read MMS data I have seen the part table in the mmssms.db where the mms entries are stored; I am using a cursor and I want to know the appropriate URI; I am using "content://mms-sms/conversations" and the Column names of "Address"(Sent to), "Text" or "Subject" and "Data" column name of image.
I have seen the schema of mmssms.db and Their Column of part Table.
share|improve this question
The mmssms.db database is part of the firmware and is not accessible by Android applications. The content://mms-sms/conversations content provider is not part of the SDK and should not be accessed by Android applications.CommonsWare Jun 10 '10 at 11:12
3
Please accept an answer…user1521536 Aug 22 '12 at 3:09

5 Answers

It's kind of difficult to find documentation about this, so I will collect here all information I have found. If you are in a rush or just don't like to read, jump to the How to get data from a SMS section.

content://mms-sms/conversations

This is the URI of the Mms and SMS provider... which allows us to query the MMS and SMS databases at the same time, and mix them in a single thread (which are called conversations).
Why is it important the content://mms-sms/conversations uri? Well, that's the standard way of getting MMS and SMS messages; for instance, when you receive a SMS and click on the notification bar, it will send a broadcast intent like this: content://mms-sms/conversations/XXX, where XXX is the id of the conversation.

Get a list of all conversations

The only thing you have to do is to query the content://mms-sms/conversations Uri:
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Uri uri = Uri.parse("content://mms-sms/conversations/");
Cursor query = contentResolver.query(uri, projection, null, null, null);
Note: usually, when you call query and want to return all columns you can pass null as the projection parameter. However, you cannot do that with this provider, so that's why I'm using *.
Now you can loop through the Cursor as usual. These are the more important columns you would want to use:
  • _id is the ID of the message. Captain obvious to the rescue? Not really. This ID can be used to retrieve detailed information using either content://sms or content://mms.
  • date no explanation needed.
  • thread_id is the ID of the conversation
  • body The content of the last SMS on this conversation. If it's an MMS, even if it has a text part, this will be null.
Note: if you query content://mms-sms/conversations it will return a list of different conversations whose _id is the last SMS or MMS in each conversation. If you query content://mms-sms/conversations/xxx it will return each SMS and/or MMS on the conversation whose ID is xxx.

How to differentiate between SMS and MMS

Usually, you will want to know which type of message you are handling. Documentation says:
A virtual column, MmsSms.TYPE_DISCRIMINATOR_COLUMN, may be requested in the projection for a query. Its value is either "mms" or "sms", depending on whether the message represented by the row is an MMS message or an SMS message, respectively.
I think it's referring to this variable... however I have not been able to make it work. If you have please tell me how or edit this post.
So far this is what I have done and it seems to work but there must be better ways:
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"_id", "ct_t"};
Uri uri = Uri.parse("content://mms-sms/conversations/");
Cursor query = contentResolver.query(uri, projection, null, null, null);
if (query.moveToFirst()) {
    do {
        String string = query.getString(query.getColumnIndex("ct_t"));
        if ("application/vnd.wap.multipart.related".equals(string)) {
            // it's MMS
        } else {
            // it's SMS
        }
    } while (query.moveToNext());
}

How to get data from a SMS

So you have the ID of the SMS, then the only thing you have to do is:
String selection = "_id = "+id;
Uri uri = Uri.parse("content://sms");
Cursor cursor = contentResolver.query(uri, null, selection, null, null);
String phone = cursor.getString(cursor.getColumnIndex("address"));
int type = cursor.getInt(cursor.getColumnIndex("type"));// 2 = sent, etc.
String date = cursor.getString(cursor.getColumnIndex("date"));
String body = cursor.getString(cursor.getColumnIndex("body"));

How to get data from a MMS data?

MMSs are a little bit different. They can be built with different parts (text, audio, images, etc.); so here will see how to retrieve each kind of data separately.
So let's guess we have the MMS id in the mmsId variable. We can get detailed information about this MMS by using the content://mms/ provider:
Uri uri = Uri.parse("content://mms/");
String selection = "_id = " + mmsId;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
However, the only interesting column is read which is 1 if the message has already been read.

How to get text content from MMS

Here we have to use content://mms/part... for instance:
String selectionPart = "mid=" + mmsId;
Uri uri = Uri.parse("content://mms/part");
Cursor cursor = getContentResolver().query(uri, null,
    selectionPart, null, null);
if (cursor.moveToFirst()) {
    do {
        String partId = cursor.getString(cursor.getColumnIndex("_id"));
        String type = cursor.getString(cursor.getColumnIndex("ct"));
        if ("text/plain".equals(type)) {
            String data = cursor.getString(cursor.getColumnIndex("_data"));
            String body;
            if (data != null) {
                // implementation of this method below
                body = getMmsText(partId);
            } else {
                body = cursor.getString(cursor.getColumnIndex("text"));
            }
        }
    } while (cursor.moveToNext());
}
It could contain different parts of text... but usually it'd be only one. So if you want to remove the loop it will work most of the times. This is how the getMmsText method looks like:
private String getMmsText(String id) {
    Uri partURI = Uri.parse("content://mms/part/" + id);
    InputStream is = null;
    StringBuilder sb = new StringBuilder();
    try {
        is = getContentResolver().openInputStream(partURI);
        if (is != null) {
            InputStreamReader isr = new InputStreamReader(is, "UTF-8");
            BufferedReader reader = new BufferedReader(isr);
            String temp = reader.readLine();
            while (temp != null) {
                sb.append(temp);
                temp = reader.readLine();
            }
        }
    } catch (IOException e) {}
    finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {}
        }
    }
    return sb.toString();
}

How to get image from MMS

It's the same than getting the text part... the only difference is that you will be looking for a different mime-type:
String selectionPart = "mid=" + mmsId;
Uri uri = Uri.parse("content://mms/part");
Cursor cPart = getContentResolver().query(uri, null,
    selectionPart, null, null);
if (cPart.moveToFirst()) {
    do {
        String partId = cPart.getString(cPart.getColumnIndex("_id"));
        String type = cPart.getString(cPart.getColumnIndex("ct"));
        if ("image/jpeg".equals(type) || "image/bmp".equals(type) ||
                "image/gif".equals(type) || "image/jpg".equals(type) ||
                "image/png".equals(type)) {
            Bitmap bitmap = getMmsImage(partId);
        }
    } while (cPart.moveToNext());
}
This is how the getMmsImage method looks like:
private Bitmap getMmsImage(String _id) {
    Uri partURI = Uri.parse("content://mms/part/" + _id);
    InputStream is = null;
    Bitmap bitmap = null;
    try {
        is = getContentResolver().openInputStream(partURI);
        bitmap = BitmapFactory.decodeStream(is);
    } catch (IOException e) {}
    finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {}
        }
    }
    return bitmap;
}

How to get the sender address

You will need to use the content://mms/xxx/addr provider, where xxx is the id of the MMS:
private String getAddressNumber(int id) {
    String selectionAdd = new String("msg_id=" + id);
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    Cursor cAdd = getContentResolver().query(uriAddress, null,
        selectionAdd, null, null);
    String name = null;
    if (cAdd.moveToFirst()) {
        do {
            String number = cAdd.getString(cAdd.getColumnIndex("address"));
            if (number != null) {
                try {
                    Long.parseLong(number.replace("-", ""));
                    name = number;
                } catch (NumberFormatException nfe) {
                    if (name == null) {
                        name = number;
                    }
                }
            }
        } while (cAdd.moveToNext());
    }
    if (cAdd != null) {
        cAdd.close();
    }
    return name;
}

Final thoughts

  • Can't understand why Google, with those thousands of millions of dollars, don't pay a student or someone else to document this API. You have to check the source code to know how it works and, which is worse, they don't make public those constants used in the columns of the database, so we have to write them manually.
  • For other kind of data inside an MMS you can apply the same idea learned above... it's just a matter of knowing the mime-type.
share|improve this answer
1
Conserning content://mms-sms/conversations. This url contains list with all threads. But not separate messages (sms or mms). So there is no sense to get to know sms or mms it is while it is none of them.Maxim Aug 8 '11 at 13:21
would there be a reason why all my MMS mime types come back as application/smil?Justin Feb 28 '12 at 23:19
1
Justin, because MMS are stored in database as slideshow using SMIL.Naba Mar 2 '12 at 5:59
@Cristian hi, i am the one who ask you in twitter. i want to get data from MMS as List so i found this tutorial,it takes ur code and add listview but when i run it. it did not show me anythingMaha Apr 10 '12 at 8:49
You better create a question and share your code with us. How can we see what you are doing wrong if we do not know your code?Cristian Apr 10 '12 at 14:50
show 11 more comments
The answer by Christian is excellent. However, the method for getting the sender's address did not work for me. The Long.parseLong statement doesn't do anything except possibly throw an exception and new String(...) ?.
On my device the cursor count is 2 or more. The first typically has a "type" of 137 and the others have a "type" of 151. I cannot find where this is documented, but one can deduce 137 is "from" and 151 is "to". Thus, if I run the method as is, I do not get an exception, and it returns the last row, which is a recipient and only one of several in many cases.
Also AFAICT the selection is not necessary as all the rows have the same msg_id. However, it doesn't hurt.
This is what works for me to get the sender's address:
public static String getMMSAddress(Context context, String id) {
    String addrSelection = "type=137 AND msg_id=" + id;
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    String[] columns = { "address" };
    Cursor cursor = context.getContentResolver().query(uriAddress, columns,
            addrSelection, null, null);
    String address = "";
    String val;
    if (cursor.moveToFirst()) {
        do {
            val = cursor.getString(cursor.getColumnIndex("address"));
            if (val != null) {
                address = val;
                // Use the first one found if more than one
                break;
            }
        } while (cursor.moveToNext());
    }
    if (cursor != null) {
        cursor.close();
    }
    // return address.replaceAll("[^0-9]", "");
    return address;
}
I didn't care about whether it is all numeric, but I included a way to eliminate everything but numerals as a comment if that is desired. It can easily be modified to return all the recipients, as well.
I assume it worked for him. It looks like it would give the right answer if the exception occurred on the first row.
share|improve this answer
I wouldn't try to call parseLong on the contact_id field; treat it as a string. In fact, it's quite possible that it could be an email address or something, in the case of email-to-mms gateways.Edward Falk Nov 22 '12 at 17:36
Take a look at this. Worked well for me.
share|improve this answer
what about application/smil type MMS messages?toobsco42 Jul 17 '12 at 7:42
The answer given above for getting the getMMSAddress() should not contain the loop while (cursor.moveToNext());. It should only extract the address from the first element in the cursor. For some reason that is unknown to me, this cursor has more than one record. The first one contains the Sender's address. The other elements of the cursor beyond the first one, contain the receiver's address. Thus the code as is return the receivers address and not the sender address.
This has been very helpful for cracking open the contents of an MMS.
share|improve this answer
I had to make some modifications in order to get this to work for me.
  1. When I retrieve the cursor.getString(cursor.getColumnIndex("type")) from the mms-sms/conversations content, ("content://mms-sms/conversations/") I test the value of the "type" field for null. If the variable is null - i.e.
    String otype = c.getString(c.getColumnIndex("type"));
    if(otype != null) {
        //this is an sms - handle it...
    the message is an SMS, else it is an MMS. For MMS's you have to test for both mime types as follows:-
    if (("application/vnd.wap.multipart.related".equalsIgnoreCase(msg_type)
        ||"application/vnd.wap.multipart.mixed".equalsIgnoreCase(msg_type))
        && !id.equalsIgnoreCase(lastMMSID)) {
             //this is a MMS - handle it...
  2. When you use a ContentObserver to monitor the message content for changes, it fires several notifications for the same message. I use a static variable - in my case lastMMSID - to keep track of the message.
  3. This code works well to retrieve the content of both Inbound and Outbound messages. It is important to iterate through all the records that are returned by the "content://mms/part/" uri in order to get to the content - text and/or attachments - of the MMS.
  4. The only way that I could find that works pretty well to differentiate between inbound and outbound MMS's, is to test the null status of the "m_id" field of the mms-sms/conversations content.
    String m_id = c.getString(c.getColumnIndex("m_id"));
    String mDirection = m_id == null? "OUT": "IN";
A final thought on how to get the Address Field. For some reason the Address Content does not like to be queried with a {" * "} parameter, but this works:-
final String[] projection = new String[] {"address", "contact_id", "charset", "type"};
If it is an outbound message, the "type" to look for will be 151. For an inbound message, the "type" will be 137. A fully functional piece of code will look something like this:-
private String getANumber(int id) {
    String add = "";
    final String[] projection = new String[] {"address","contact_id","charset","type"};
    final String selection = "type=137 or type=151"; // PduHeaders
    Uri.Builder builder = Uri.parse("content://mms").buildUpon();
    builder.appendPath(String.valueOf(id)).appendPath("addr");

    Cursor cursor = context.getContentResolver().query(
        builder.build(),
        projection,
        selection,
        null, null);

if (cursor.moveToFirst()) {
          do {
              String add = cursor.getString(cursor.getColumnIndex("address"));
              String type: cursor.getString(cursor.getColumnIndex("type"));
          } while(cursor.moveToNext());
      }
      // Outbound messages address type=137 and the value will be 'insert-address-token'
      // Outbound messages address type=151 and the value will be the address
      // Additional checking can be done here to return the correct address.
      return add;
}
To all the brave warriors who have gone before me in this post - I thank thee from the bottom of my heart!
share|improve this answer