Wednesday, March 6, 2013

Recovering from bad overclocking settings

My android phone is nearly three years old, it's a tiny thing from the days before screen sizes started growing ridiculously large.

I've held onto it because I don't like large phones, the problem is that it is quite underpowered by today's standards: 600 MHz cpu, 256MB of RAM.

Sony Ericsson stopped upgrading this phone at Eclair, but thanks to the dev community, I'm currently running Jelly Bean 4.1.2 on a custom kernel.

Tweaking my phone has become something of a hobby and in an effort to squeeze every drop of performance out of this old hardware I sometimes experiment with overclocking the processor.  This kind of customization is built right into the Cyanogen ROM (presuming your kernel supports it), and the settings page comes with a big warning.

It is a very good idea to not select "Set on boot" until you know that the settings you have chosen will not crash your phone.  If bad settings crash your phone and are restored on boot, then you can end up in a crash/boot loop.

So, one night I noticed that a recent kernel upgrade had increased the upper limit for the maximum CPU frequency to 864MHz.  With total disregard for the consequences, I decided to try this out and forgot to unset the "Set on boot" option.

864MHz is too high and the phone crashes after about 10 seconds.  After rebooting, you have again maybe 10 seconds before it crashes again.  This is not enough time to get back to the settings page and restore things to working order.

Unfortunately, the last full backup of my phone was about a week older than I liked and I really didn't want to lose the changes I had made since then by doing a restore.

The first step towards fixing this problem was to look at the source code for the settings app which is available on github.  A peek at reveals the relevant preference keys:
    public static final String FREQ_MIN_PREF = "pref_cpu_freq_min";
    public static final String FREQ_MAX_PREF = "pref_cpu_freq_max";
    public static final String SOB_PREF = "pref_cpu_set_on_boot";
We can see in that those preferences are read on boot and applied to the cpu.

Step two is to boot into CWM recovery and mount the /data partition.  On my phone you can enter recovery by repeatedly pressing the back button when the kernel boot animation first starts up.

Connect your phone to a pc via USB cable and start an adb shell.

The settings app will be storing its data under /data/data/
/data/data/ # ls -la
drwxr-x--x    1 system   system        2048 Mar  2 02:32 .
drwxrwx--x    1 system   system        2048 Mar  6 05:01 ..
drwxrwx--x    1 system   system        2048 Mar  2 04:04 cache
drwxrwx--x    1 system   system        2048 Mar  2 02:37 databases
drwxrwx--x    1 system   system        2048 Feb 26 05:15 files
drwxr-xr-x    1 system   system        2048 Feb 26 04:59 lib
drwxrwx--x    1 system   system        2048 Mar  6 16:34 shared_prefs

/data/data/ # ls shared_prefs

Initially I was expecting to have to break out some sql and modify database files, but the file immediately caught my eye. grep shows us the cpu preference settings there in plain xml.

/data/data/ # grep pref_cpu
<boolean name="pref_cpu_undervolt" value="true"/>
<string name="pref_cpu_freq_max">864000</string>
<boolean name="pref_cpu_set_on_boot" value="true">
<string name="pref_cpu_freq_min">122880</string>
<string name="pref_cpu_gov">smartassV2</string>
While we are still in the adb shell we can also check to see what the valid frequency values are:
~ # cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
122880 245760 320000 480000 604800 614400 633600 652800 672000 691200 710400 
729600 748800 768000 787200 806400 825600 844800 864000
All that remains is to adb pull the file to the PC, modify the pref_cpu_freq_max setting to something more reasonable (729600), and then adb push it back to the phone.