What about a brand new Android phone as a birthday gift for your girlfriend? It is cool if she sees something different when she opens the box and turn on the device for the first time, isn't it? If you are happy with this idea, let's go.
WARNING: SOME MODIFICATIONS MADE TO YOUR PHONE IN THIS TUTORIAL MAY CAUSE YOU LOSE YOUR WARRANTY AND EVEN BRICK YOUR PHONE. DO IN YOUR OWN RISK.
Step 1. Prepare your phone for system modification.
For an HTC phone, S-OFF your phone to turn off the runtime system protection.
Ref: http://revolutionary.io/
At the end of the S-OFF procedure, you will be asked about flash the ClockworkMod recovery image. Good to do so to get a customized recovery program which is easy to use. Better "root" your phone to gain max control over your phone.
Ref: http://unrevoked.com/
Actually, the most significant step in the "rooting" procedure is installing the "su" command into the system. Your can simply download an "su" ARM binary from a trusted source or even compile your own, than copy it into the /system/bin directory. There are plenty of information about S-OFF and "rooting" your phone, so I don't want to spend more words here.
Step 2. Create your own OOBE app.
OOBE, or Out of Box Experience, is a run-once application to guide you get your phone provisioned. Have you mentioned that the first time you boot up your Android phone, you are asked to fill in information about your Google account and do some settings to customize your phone. It disappears after all set. What we wanna do here is to insert our own app before this OOBE procedure is started. Let's start with create a normal app first and then do some tricks on it to meet the requirement.
Tons of books are discussing Android application development today. Simply grasp any of them and you can easily create a very compact application with only one activity, showing a bunch of roses with some nice words over it. Simple but full of love. I created my own app for example.
Application Name: HappyBirthday
Package Name: com.zxhmike.happybirthday
Default Activity: HappyBirthdayActivity
Just do anything you want in the HappyBirthdayActivity. Usually some pictures are added to the related "main.xml" located in the /res/layout directory of the project.
Now here is the trick.
Add an intent to the HappybirthdayActivity in the file AndroidManifest.xml. (Line 17 - 21)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zxhmike.happybirthday"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".HappyBirthdayActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:priority="2">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
The intent filter [Action: Main Category:Launcher] (Line 15 - 18) is only necessary in debug. Delete it before uploading the app to the phone.
Add a button labeled "I Love You" or anything else to the "main.xml" under "res/layout" directory. This screen will disappear and "never" come back when your girlfriend clicks it. Add the following code to the activity.
package com.zxhmike.happybirthday;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class HappyBirthdayActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final PackageManager pm = getPackageManager();
final ComponentName cn = new ComponentName(getApplicationContext(), this.getClass());
Button button = (Button)findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
}
});
}
}
Step 3. Compile your app and upload to your device.
Compile your app. If using Eclipse with ADT, the app will be automatically uploaded to your phone or emulator. When the "Home" button is pressed, a dialog will show to let you choose either switch to the launcher app or the "HappyBirthday" app. It is wield this stands out even our app is in a higher priority. This is because our app is not put into "/system/app" directory. Let's upload our app to this directory instead.
adb remount
adb push HappyBirthday.apk /system/app
Use the following code instead if the first command above doesn't work.
adb shell
su
mount -o remount,rw /system
exit
Step 4. Try it!
Time to reboot the phone. The highly customized OOBE will show. But it disappears "forever" once clicked the "I Love You" button.
TIP: use the "pm" command to get it back.
pm enable com.zxhmike.HappyBirthday/.HappyBirthdayActivity
We are all set. Enjoy! If you are willing to know how this is happening behind the scene, follow up!
How could this happen?
Look back to the "AndroidManifest.xml"
The intent filter [Action: Main Category:Home, DEFAULT] is the same intent as the one to switch to your home screen. The system app "launcher", which is in charge of the home screen and widget loading, contains the same intent filter in its "AndroidManifest.xml".
When the phone first boots, the Android system doesn't know that this is the first time it sees the world, and it sends out the intent "intended" to launch the home screen. Unfortunately, the OOBE app takes the handle because it also holds the same intent filter [Action: Main Category:Home, DEFAULT] in its "AndroidManifest.xml" with higher priority of "1". After the phone is provisioned (That means you set the time, chose the language ...), the OOBE app disables itself. The next time the Android system sends out the intent [Action: Main Category:Home, DEFAULT], the home screen is showed instead, which is the usual case we see.
With a higher priority "2" in the intent filter for [Action: Main Category:Home, DEFAULT], our homemade OOBE app is launched even before the default OOBE app when the phone first starts. This is what the trick is.
Then, a few words on how to disable the app itself. In short, use the PackageManager API, as in the code.
Finally, why should the app be put into the "/system/app" directory? Because priority doesn't count if it is not a system app. See the source code of Android:
// ICS 4.0.1 PackageManagerService.java Line 4362
if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
intent.setPriority(0);
Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
+ a.className + " with priority > 0, forcing to 0");
}
The priority is forced to zero if it is not a system app which should be located in "/system/app" directory.
It evolves some Android source code reading and app reverse engineering to get there. Next time I will introduce basic reverse engineering of Android app. Like Linus said, "Read the f** source code!"
ENJOY!
Mike Zou

This work is licensed under a Creative Commons Attribution 3.0 Unported License.