Android your (my) way

During my work I was requested by my boss to create a cellular site survey application for Android.
The requirements were simple: display the cell id, location area code, signal level, network type (2G/3G), a ping button and a lock on 2G/3G buttons.

Android has an internal application (you can call it technician code, or whatever) that gives you all of that stuff, but is not user-friendly.
You can see it on your own, simply dial “*#*#4636#*#*” on your Android phone, then navigate to “Phone information”.

Basically, my goal is to get the same app, but with a simpler, more user-friendly interface. I don’t need a lot of stuff in there on my application.

The radio information stuff (signal, cid, lac, network type) are simple. They’re exposed through the API for easy query. The 2G/3G lock with a single button click is the tricky part.

First, I searched for the implementation of the “Phone Information” activity source code (com.android.settings.RadioInfo), which is available here.
Taking a look at it, you’ll find this:

AdapterView.OnItemSelectedListener
        mPreferredNetworkHandler = new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView parent, View v, int pos, long id) {
        Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE);
        if (pos>=0 && pos            phone.setPreferredNetworkType(pos, msg);
        }
    }

Moving us to the phone object, which is of type com.android.internal.telephony.Phone, initialized with com.android.internal.telephony.PhoneFactory().
This is a problem, we don’t have access to com.android.internal packages. Google search yields this blog post. Follow the instructions there to add com.android.internal packages to your Eclipse environment.

The problem is, you’re not able to do the following on your code:

Phone phone = PhoneFactory.getDefaultPhone();

Without it having

android:sharedUserId="android.uid.system"

on the manifest’s <manifest> tag, and

android:process="com.android.phone"

on your <activity> tag. From the Android manifest documentation:

android:sharedUserId
The name of a Linux user ID that will be shared with other applications. By default, Android assigns each application its own unique user ID. However, if this attribute is set to the same value for two or more applications, they will all share the same ID — provided that they are also signed by the same certificate. Application with the same user ID can access each other’s data and, if desired, run in the same process.

Also, from the <application> manifest element documentation:

android:process
The name of a process where all components of the application should run. Each component can override this default by setting its own processattribute.By default, Android creates a process for an application when the first of its components needs to run. All components then run in that process. The name of the default process matches the package name set by the <manifest>element.By setting this attribute to a process name that’s shared with another application, you can arrange for components of both applications to run in the same process — but only if the two applications also share a user ID and be signed with the same certificate. If the name assigned to this attribute begins with a colon (‘:’), a new process, private to the application, is created when it’s needed. If the process name begins with a lowercase character, a global process of that name is created. A global process can be shared with other applications, reducing resource usage.

Having this on the manifest means your application runs as a system user on the same process with com.android.phone, requiring your application to be signed with the platform certificate. Oh no.

Luckily, I’m running CyanogenMod, which you can obtain the platform key & certificate pretty easily.

Compile your android APK with Eclipse just as you would for any other application. Uploading it will fail because of the sharedUserId being “android.uid.system”. Don’t worry, we’ll fix it right away.
Grab signapk.jar, create a directory with the following files:

  1. SignApk.jar
  2. YourApplication.apk
  3. platform.x509.pem
  4. platform.pk8

Next, open a shell (cmd.exe on Windows) and navigate to that directory.
Issue the following command:

java -jar signapk.jar platform.x509.pem platform.pk8 YourApplication.apk YourApplication-signed.apk

If you’ve done everything right, a new file, “YourApplication-signed.apk” would be created in that same directory, signed with the platform certificate.
The next step is to push it into the device and launch your activity. Make sure adb is in your path, or replace “adb” with “/path/to/adb” (or “C:\path\to\adb.exe” on Windows) on these commands:

adb shell mount -o remount,rw /system # this remounts the system partition read-write
adb push YourApplication-signed.apk /system/app/YourApplication.apk # this pushes your application to the system apps folder
adb shell mount -o remount,ro /system # this remounts the system partition read-only

Launch your activity:

adb shell am start -a android.intent.action.MAIN -n com.your.package/.YourActivity

Good luck!

Omri.

3 Responses to Android your (my) way

  1. Daniel says:

    very useful!
    eager to open eclipse and have a go.

  2. I like it when individuals come together and share ideas.
    Great blog, keep it up!

  3. Shlomo Kut says:

    Don’t forget to run zipalign after signing as the signing processes leaves the APK unaligned and it will NOT install. You will need to install the aligned APK.
    zipalign -fv 4 YourApplication-signed.apk YourApplication-aligned.apk

Leave a reply to Daniel Cancel reply