Android Klaxon Update

Added a couple new features this morning to the Android version of Klaxon:

  • Customize your alarm volume.
  • Implemented the new feature: increasing alarm volume.
  • Fixed bug where snooze was sometimes not snoozing for the correct amount of time.

Grab it off the Market!

Update to Superuser is now compatible with all Android Applications

Edit: Supersuer is no longer on the Market. It is now a part of JesusFreke's rooted G1 images!

I've updated Superuser to work seamlessly with any application that may need access to su: when those application attempt to execute su, the user will see the following:

suconfirm

You will see the User ID, and any processes associated with that uid. Selecting yes will allow that uid to execute su once, while selecting always will put it on a permanent white list.

Get it off the Android Market, or download it directly.

Installation instructions are straightforward, simply install Superuser and it will take care of updating your system files and closing the security hole. If installation fails. Use the direct download and follow the instructions below:

  • Make sure you have the adb tool from the SDK.
  • Unzip the ZIP file (it will create a folder called Superuser).
  • Run install.bat (or install.sh if you are on Linux) from the Superuser directory.

Android Screenshot Application

After taking a quick peek through the Android source code, I figured out how to write an application that takes a screenshot of whatever is shown on the phone. However, you must have root to use this application.

Download it off the Market!

Usage:

Start up the application:
ss1

Choose the amount of time to wait before taking the screenshot:

ss2

Once the screenshot is taken, you will get a toast on the bottom of the screen letting you know:

ss3

Go back to the Screenshot application and view the screenshot and save it out:

ss4

Android Market Bug?

Several users of the Shell application are reporting that Shell is somehow reinstalling itself after uninstall: this includes people who do not have rooted G1s. I frankly have no idea how this is even possible, other than it being a bug in the Android Market application: Android Market is the only Android application that has permissions to install other applications. Suffice to say, I'm looking into the issue.

Drawing Text Overlays using the Tiled Map Client

(and the internal workings of the Tile Rendering Engine)

TextMapDrawable

This morning, I got an email from one of the users of the Tiled Maps library. He pointed out that although it was easy to place Bitmap overlays at a position on the map, he couldn't figure out how to draw text at that position. His approach was to draw text on a Bitmap and then draw that Bitmap onto the map. The problem he was running into, however, was having a proper transparent background on that Bitmap (Windows Mobile does not support an alpha channel). Although it is possible to do a masked blit in Windows Mobile, this method of drawing text onto a map is not ideal.

If one examines the internals of the Tiled Map Client, you will find that overlays that appear on the map actually have a very flexible abstraction layer around them. The TiledMapSession itself has no internal knowledge of the overlay renderering implementation, in that it does not actually perform the drawing. It is only concerned about the Width and Height, so it can perform proper layout to let the rendering engine (IMapRenderer) to draw the content at the proper location. This is how the Tiled Map Client is flexible enough to perform both 2D and 3D rendering:

/// <summary>
/// IMapRenderer provides the methods necessary for a TiledMapSession
/// to draw tiles to compose the map, as well as the other content
/// that may appear on the map.
/// </summary>
public interface IMapRenderer
{
    /// <summary>
    /// Get a IMapDrawable from a stream that contains a bitmap.
    /// </summary>
    /// <param name="session">The map session requesting the bitmap</param>
    /// <param name="stream">The input stream</param>
    /// <returns>The resultant bitmap</returns>
    IMapDrawable GetBitmapFromStream(TiledMapSession session, Stream stream);

    /// <summary>
    /// Given a IMapDrawable, draw its contents.
    /// </summary>
    /// <param name="drawable">The IMapDrawable to be drawn.</param>
    /// <param name="destRect">The destination rectangle of the drawable.</param>
    /// <param name="sourceRect">The source rectangle of the drawable.</param>
    void Draw(IMapDrawable drawable, Rectangle destRect, Rectangle sourceRect);

    /// <summary>
    /// Draw a filled rectangle on the map.
    /// </summary>
    /// <param name="color">The fill color.</param>
    /// <param name="rect">The destination rectangle.</param>
    void FillRectangle(Color color, Rectangle rect);

    /// <summary>
    /// Draw a line strip on the map.
    /// </summary>
    /// <param name="lineWidth">The width of the line stripe.</param>
    /// <param name="color">The line strip color.</param>
    /// <param name="points">The points which compose the line strip.</param>
    void DrawLines(float lineWidth, Color color, Point[] points);
}

/// <summary>
/// IMapDrawable is the interface used by the Tiled Map Client to represent content
/// onto a IMapRenderer.
/// IMapDrawable is generally tied to an implementation of IMapRenderer, 
/// which is responsible for internally representing and rendering the drawable.
/// </summary>
public interface IMapDrawable : IDisposable
{
    /// <summary>
    /// The width of the drawable content.
    /// </summary>
    int Width
    {
        get;
    }

    /// <summary>
    /// The height of the drawable content.
    /// </summary>
    int Height
    {
        get;
    }
}

So, how do we go from drawing a bitmap to drawing text? The standard/normal implementation and usage of an IMapRenderer is the GraphicsRenderer. The GraphicsRenderer is what facilitates rendering of a TiledMapSession to a System.Drawing.Graphics instance. Let's take a look at it's implementation of Draw:

public void Draw(IMapDrawable drawable, Rectangle destRect, Rectangle sourceRect)
{
    IGraphicsDrawable graphicsDrawable = drawable as IGraphicsDrawable;
    graphicsDrawable.Draw(Graphics, destRect, sourceRect);
}

As you can see, it casts the IMapDrawable to an IGraphicsDrawable and calls its implementation of Draw, passing it the Graphics object:

/// <summary>
/// IGraphicsDrawable is a type of IMapDrawable that can draw to a
/// System.Drawing.Graphics instance.
/// </summary>
public interface IGraphicsDrawable : IMapDrawable
{
    void Draw(Graphics graphics, Rectangle destRect, Rectangle sourceRect);
}

There are two provided implementations of IGraphicsDrawable: WinCEImagingBitmap, which uses the Imaging API to draw bitmaps that contain alpha, and StandardBitmap, which draws a standard System.Drawing.Bitmap. So what we need, is a third implementation, which I called TextMapDrawable. TextMapDrawable will implement IGraphicsDrawable and use Graphics.DrawString to draw text onto the Graphics object.

Here's my implementation of TextMapDrawable:

public class TextMapDrawable : IGraphicsDrawable
{
    static Bitmap myMeasureBitmap = new Bitmap(1, 1, PixelFormat.Format16bppRgb565);
    static Graphics myMeasureGraphics = Graphics.FromImage(myMeasureBitmap);

    public float MaxWidth
    {
        get;
        set;
    }

    public float MaxHeight
    {
        get;
        set;
    }

    Brush myBrush;
    public Brush Brush
    {
        get
        {
            return myBrush;
        }
        set
        {
            myBrush = value;
        }
    }

    bool myDirty = true;
    string myText;
    public string Text
    {
        get
        {
            return myText;
        }
        set
        {
            myText = value;
            myDirty = true;
        }
    }

    Font myFont;
    public Font Font
    {
        get
        {
            return myFont;
        }
        set
        {
            myDirty = true;
            myFont = value;
        }
    }

    #region IGraphicsBitmap Members

    public void Draw(Graphics graphics, Rectangle destRect, Rectangle sourceRect)
    {
        // just ignore source rect, doesn't mean anything in this context.
        if (CalculateDimensions() && myBrush != null)
            graphics.DrawString(myText, myFont, myBrush, destRect.X, destRect.Y);
    }

    #endregion

    bool CalculateDimensions()
    {
        bool valid = !string.IsNullOrEmpty(myText) && myFont != null;

        if (myDirty)
        {
            myDirty = false;
            if (valid)
            {
                SizeF size = myMeasureGraphics.MeasureString(myText, myFont);
                myWidth = (int)Math.Ceiling(size.Width);
                myHeight = (int)Math.Ceiling(size.Height);
            }
            else
            {
                myWidth = 0;
                myHeight = 0;
            }
        }
        return valid;
    }

    #region IMapBitmap Members

    int myWidth;
    public int Width
    {
        get
        {
            CalculateDimensions();
            return myWidth;
        }
    }

    int myHeight;
    public int Height
    {
        get
        {
            CalculateDimensions();
            return myHeight;
        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
    }

    #endregion
}

As you can see, it took only 38 lines of code (according to Visual Studio's Code Metrics) to allow drawing of text to the map! I have also updated the Tiled Map Client source for those interested in these changes.

Fixing the "setuid su" security hole on Modified Android RC30

shell

If you have a rooted Android phone, you are probably using a variation of JesusFreke's RC30 image. With JF's image, there are two ways you can get root:

  • For remote root, you can adb shell into the phone
  • For local root, run the root setuid su from Terminal Emulator or pTerminal.

The problem with the latter version is that any application can run su and get full unfettered access to everything on the phone. This leaves the door open to malicious applications. However, the answer is not simply removing the su file, as then there is no way to perform superuser tasks from within legitimate applications.

To that end, I wrote the Superuser application that fixes the security hole, and also allows you or any application to get root when properly authorized. I've also written a Shell application to demonstrate how an application can request authorization.

How Superuser sets itself up:

  • The standard RC30 install will have a setuid /system/bin/su. (if you deleted disabled it, reenable it for setup)
  • Install the Superuser Java application to your phone and run it.
  • Superuser will create a copy of su named superuser.
  • Superuser will chown superuser to user "root" and group app_gid (where app_gid is the group id of the Superuser application as determined by Android)
  • The superuser binary will also be chmod 4750 superuser, so that the Superuser Java application, and only that application, can execute it as root.
  • Finally, Superuser will chmod 4700 su to close the security hole.

How other applications get root access with the user's permissions:

  • Any application can fire an intent to request access to the locked down /system/bin/su.
  • When that happens, Superuser will catch the intent and ask the user if it should grant that application root permissions.
  • If allowed, Superuser will chown 0:app_gid /system/bin/su (where app_gid is the gid of the requesting application).
  • That application can then use /system/bin/su as normal.

Notes:

  • /system/bin/su will get reset to chown 0:0 after 10 seconds, so the requesting application must start the instance of su up within that grace period. This is a bit of a kludge, but I'm a Linux newbie and don't know a better/cleaner way to do it.
  • Since this is an unmodified version of su, 3rd party applications don't have to worry about piping in passwords and such.
  • This is using the standard Linux level permissions and restrictions, as well as the Android framework permissions present at the Java level.

Full source code to Superuser and Shell are at the bottom of this post.

So, here's what it the user experience looks like when running Shell and requesting root access:

First, we start up shell and try to run su. Note that running su is not allowed (as the uid of the process did not change). Shell needs to request permission to access su first:

nosu

This menu button fires the intent to request permission to su:

surequest

Superuser receives the request and asks the user if they wish to grant Shell superuser permissions:


suconfirm

Once granted, the user can properly execute su (as indicated by the root id in the prompt):

suworking

 

Superuser and Shell APK install files (will be on the market too soon).

Superuser and Shell Source Code.

Code and Applications

All code found on this site is licensed under the Do What The Fuck You Want To Public License (WTFPL). Yes, it is a legitimate license.
Interested in seeing all my blog entries about open sourced code? Here you go!

(This page is generated in real time, with JavaScript running on your browser, by the GithubProjects code. So if you can't see any projects, get a better browser!)

To check out a project, type the following git commands to get the project and all its dependencies:
git clone git@github.com:koush/ProjectName.git
cd ProjectName
git submodule init
git submodule update

Klaxon - Samsung Light Sensor Update

Version 2.0.0.29

  • Updated Klaxon to support Samsung's Light Sensor
  • Fixed issue with device not waking up from sleep to sound alarm.

Here's the download!

Unified Sensor API now supports the Omnia Light Sensor

Around a month ago I got a tip from an anonymous reader (I think it's a Samsung developer) on how to access the Samsung's light sensor. The tip seems to work correctly [0], so I added support for that sensor in the Sensor API.

I ended up having to refactor the API a little to support the Samsung light sensor transparently: the changes are similar to the ones I made a few months ago for transparently creating the proper IGSensor implementation on a given device.

So, the change log:

  • HTCLightSensor is now a private class.
  • SamsungLightSensor is a new private class.
  • Refactored HTCGSensor and SamsungGSensor to derive from GSensorBase which inherits from PollingSensor. This change was made to consolidate much of the sensor code.
  • Instead of creating an HTCLightSensor or SamsungLightSensor explicitly, developers should use the new LightSensorFactory.CreateLightSensor method to get the appropriate ILightSensor object for the phone the application is running on.

Here's the link for the updated API and code samples.

I'll be recompiling Klaxon shortly to accommodate the new changes.

[0] It looks like the way this is done is by reading the backlight intensity value, rather than actually reading the light sensor. The Omnia sets the backlight value based on that reading.

Android Debug Bridge - x64 Windows Drivers Released!

Three weeks ago, I blogged about how I used VMWare and Ubuntu to develop and debug Android applications on my Windows Server 2008 x64 installation. I had to do this because Google did not write a 64 bit ADB driver for Windows. Thankfully, since the entire Android platform is open source, aziwoqpd from xda-developers undertook the task of porting the 32 bit drivers to a 64 bit platform. Been playing with with this for a bit, and it's been working great! Now all that's left is for someone to port the repo command to Windows, and I can get rid of this Ubuntu VM altogether.

Visit the linked forum post for the driver download.

Open source gives me a warm fuzzy feeling sometimes. But I still think GPL is lame.

Taking Screenshots on your Android Phone

A few weeks ago, I dabbled around with trying to write a screenshot application for Android. Turns out that it is not possible due to security/sandbox restrictions: your screenshot code can only take snap screens that are within the same application. That pretty much sucks. I think it is doable now since that the G1 has been hacked, and root access is available.

It is possible, however, to take screenshots through the DDMS tool (Dalvik Debug Monitoring Service).

  1. Install the Android SDK.
  2. Enable USB debugging on your phone.
  3. Run ddms from the tools directory of the Android SDK.
  4. Select your phone from the list on the left.
  5. Go to the menu and select Device, then Screen capture.

Klaxon - An Accelerometer Enabled Alarm Clock for Android

KlaxonMain KlaxonAlarmEdit KlaxonRingtoneType KlaxonClockTypes KlaxonAlarm

This is the Android version of Klaxon, the popular Windows Mobile Alarm Clock. In addition to the standard features of the default Android Alarm Clock application, Klaxon has the following features:

  • Flip your phone over to snooze it.
  • Wake up to a song stored on your SD card.
  • Customize your snooze time.
  • Additional clock faces.

You can find Klaxon on the Android Market! And for those of you that can't access the market, you can download the Android version of Klaxon here.

Post 11: Android versus Windows Mobile Development

I wanted to refrain from blogging about this until I had at least somewhat delved into Android development. And although I am by no means a guru at the Android platform yet, I feel I can safely make some broad statements, step on some toes, and be able to sufficiently back up my opinions. [0] :)

 

The Byte Code

Google made some interesting architectural and strategic decisions regarding Android. The most notable being Java as the development language and Dalvik as the underlying bytecode. Dalvik, in short, is a byte code that is interpreted at runtime, similar to Sun's Java byte code or Microsoft's Intermediate Language (aka IL)[1]. I didn't really understand why Google would go and create another byte code specification for Java until I read a great article by Stefano Mazzochi. The long and short of it is, Sun has some nasty licensing around Java ME, and the "Open" Handset Alliance can't really have an Open platform with the possibility of some mega-corporation trying to wet their beaks at their expense. [2]

So, still begs the same question: why invent a whole new byte code when Intermediate Language has no such licensing (it is an ECMA approved standard)?  Maybe Google took a look at it and decided it wasn't suitable for a mobile platform, or maybe they didn't want to jump into bed with Microsoft. I'm guessing the latter.

 

The Language

At the end of the day, the byte code generated behind the scenes is about as important as the color of my socks. I generally don't think about it on a daily basis, and what really matters is the language you are writing in. I can understand that Google chose Java to ease their target developer base's transition the new platform, but still... I'm going to just go ahead and say it. Java sucks.

How much does Java suck? Let me count the ways.

  1. Terrible constraints around package/file/directory/class names.
  2. Lack of anonymous methods.
  3. Lack of closures.
  4. No lambda expressions (or LINQ).
  5. Lack of events/delegates.
  6. Lack of partial classes.
  7. Ridiculous implementation of enumerations.
  8. Terrible implementation of generics (type erasure).
  9. No user defined value-types: no "struct" and thus no capability of having objects that live purely on the stack. This is nasty on performance when it counts (such as in games).
  10. Strings are immutable, but not interned.
  11. No access to "unsafe" code.
  12. No get/set accessor methods.
  13. No operator overloading.
  14. No extension methods.
  15. Last but not least, stupid and unintuitive brace positioning.

How many ways? 13, 14, 15, Ha, Ha, Ha, Ha, Ha!

 

I'm not trying to be a nit-picky zealot here; everything I listed is a feature I use on a regular basis. I will admit though, that I am enjoying anonymous classes to some degree, but they would be mostly pointless with the availability of events/delegates. I realize that most of what I listed is just syntactic sugar, and neither Java or C# is more "powerful" than the other; they can each do anything you need in their own way. Except C# lets you do it more quickly, intuitively, and elegantly. [3]

 

The Development Environments

Eclipse is a giant steaming pile of... yeah. Yes, very capable, very flexible, but whoever is doing usability testing on this junk needs to get a job that involves testing the constricting force of a rope around their neck.

My biggest pet peeve is the fact that you can't simply click in the Expressions view and edit or add something inline. You have to right click it and click add/edit. That takes you to a vanilla text editor (which has no Intellisense/Content Assist), type in your expression, pray it is parsable, and then click ok. Otherwise you are having to go through that zillion step process yet again.

Earlier I gave some kudos to Java's inner class capabilities. As neat as they are, Eclipse makes them damn near unusable: you can't watch a value of a member variable in the parent class of an inner class. That makes debugging a giant pain: you need to load all those members into local variables if something is going wrong, otherwise it is impossible to find and fix even simple issues, such as a null reference. I'm not sure if this is a problem with Eclipse, Java, or Android though.

The auto-complete in Eclipse is painfully annoying. Auto-complete should be optional, and not forcibly inserted with no regard to what the developer wants. I ended up turning it all off, because what it was inserting was generally useless. And even though the auto-complete is off, Eclipse is still inserting parenthesis for me...

 

The SDKs 

Though I have been complaining a lot about developing on Android, I do want to point out that I haven't actually complained about Android. Basically every API I could think of was standardized and implemented.

My litmus test in this regard was a port of Klaxon to Android. It really helped underscore the huge differences between the two platforms.

For me, most notable was the APIs to access device sensors. Version 1 of the Android SDK exposes a simple SensorManager class that took me 5 minutes to figure out and access. Microsoft has had 6ish versions of Windows Mobile with no Sensor API in sight.

Secondly, scheduling processes to start on Android is infinitely easier than in Windows Mobile. Here's how that learning process worked for me on Windows Mobile:

  • First I started with CeRunAppAtTime. I write a gross PInvoke for this. This works for a while, then stops working completely. Others have had this issue as well. I need to switch to another mechanism.
  • Then I switched to CeSetUserNotificationEx. I write yet another gross PInvoke for this.
  • This involves filling out some elaborate UserNotificationTrigger structure. I eventually get this working.
  • Then I find that if the device is sleeping, Klaxon sometimes does not start properly.
  • I solve this issue by digging through some documentation and randomly figuring out the problem: I need to force the device to turn on or it goes back to sleep.
  • Klaxon works again for a while, until daylight savings occurs. On some phones, it is now firing an hour late. I have no idea why. I have given up.

Let's compare this to Android:

  • Access the AlarmManager.
  • Tell it to turn on and fire an intent at a certain time.
  • Handle the intent.

This just works, no hassle, no fuss. I must say, intents are easy to use, amazingly flexible, and equally powerful.

So, two of the "difficult" parts for creating Klaxon on Windows Mobile were not so difficult at all on Android. The final piece: UI implementation.

Windows Forms code versus Android's XML Layout. Windows Forms which works with absolute sizing and positioning, and Android which uses relative sizing and positioning. Windows "Static" Controls versus Android's Animatable Controls. There really is no comparison, so I won't even bother.

Simply put, the relevant Android SDK blows the Windows Mobile SDK out of the water (language and framework features aside).

Windows Mobile does have far better documentation (MSDN) than Android, but I consider that more of a teething issue that would occur with any new SDK.

 

The Platforms

Android is pretty heavily locked down from a security standpoint. Applications must register what capabilities they wish to have, and the user must approve of them. And though this security is all in all a positive thing, it does have it's downsides; for example, it is impossible to write an application that screen shots a screen other than its own, as this is considered a security risk. [4]

And though this is not really a fault of Android per se, the phones that are actually being released are not as "open" as the name Open Handset Alliance would suggest. Carriers always have and will continue to lock down phones so long as they subsidize them. For example, the user does not have access to the root account on the G1; this makes removal/tweaking of the standard Android applications impossible. But at least on Android you can tweak the standard applications due to it being open source (after getting root).

On the flip side of the coin, Windows Mobile doesn't have a VM hiding all the nitty gritty details from you. Developers can easily get low level access to anything, without having to worry about Microsoft filling the holes. For example, if Android were to have shipped without a Sensor API, there is no way (outside of manually installing native binaries through ADB) that a developer could implement it. And that is not a comforting feeling. For example: Android does not support streaming video through the MediaPlayer yet, and as far as I know, nothing can be done about that unless the native android binaries are patched through an OTA update.

Oh, and the Android Market. Not going to bother elaborating on this point, as the statement itself is pretty self explanatory.

 

Summary

I don't really have anything insightful to say here, and that may be because it is 4AM. I'll wrap it up by saying, I really enjoy developing for Windows Mobile. The language and tools are great. However, the platform and applications simply are not very usable. On the other hand, though I'm not completely discontent with Android development, the platform, applications, and general usability is fantastic; for once I don't have to fight my phone to do simple things like browse the web. So you can guess which phone is always in my pocket. And then you can infer which once I am more interested in developing in.

That said, I hope Microsoft can produce a usable platform in the near future; as that would make me a very happy user and developer. I can't believe I'm going to say this, but they should take a page out of Apple's book and just say "screw backwards compatibility, screw existing applications, we're going to develop a brand new platform that works". They've been catering to the backwards compatibility mantra for so long that it is impossible for them to move forward. And all the while, they are alienating the very market share they are so afraid to lose.

 

 

[0] I've released 2 applications on the Market, Telnet and Klaxon, so I feel I have a decent grasp of developing for the Android platform. Klaxon has really helped underscore the huge differences in the platform.

[1] Yes, I realize that IL is not interpreted on the Windows Platform, it is JIT compiled.

[2] As "evil" as Microsoft may be connoted to be, you don't see them on the news exercising their patent rights. Unlike Sun.

[3] I'm thinking about looking into Ruby.

[4] Maybe Google will implement this in the future.

Android Telnet Client

telnet

After starting telnetd on your G1, naturally the next thing you want to do is to telnet into it. Android did not have a telnet application, so I quickly whipped up a raw telnet client. Download it from the Android Market!

Here's a direct download to the Telnet.apk.

Root Access on the TMobile G1 and why it works

In my previous post, I received a comment that included a link explaining why this all works.

To translate: anything the user types on the keyboard is actually being fed into a console shell and potentially executed. So the easiest way to get root on your phone is to simply reboot it, and when it finishes rebooting, type "telnetd" and press enter. Your phone will start doing a contact lookup in your UI, but at the same time it is typing into a root shell.

This is basically a huge oops on Google's part! Looks like some debug related settings that they never took out.

So really, pTerminal is not necessary at all in this hack, but it it accidentally exposed it!

Note:

There is an easier way to do it: press enter (this runs/clears whatever may be pending on the console), type telnetd, press enter. Although this may not work if the console is blocked by another operation. That's why a reboot is recommended: to forcibly exit the pending process.

How get root access on the T-Mobile G1

android_is_live_feature

Instructions:

  1. Turn on your phone's WiFi. This gives your phone an IP you can reach it at freely.
  2. Get to a command prompt on your device by using the PTerminal application from the Android Market. (adb shell does not seem to work with these instructions, telnetd does not start up)
  3. cd system
  4. cd bin
  5. telnetd
  6. netstat (get your phone's IP)
  7. telnet into your phone from your computer

You now have root! I verified this by remounting the /system partition as read/write and changing files around:

mount -oremount,rw /dev/block/mtdblock3 /system

That remounts the /system directory as read/write instead of read only. This can only be done as root.

WARNING:

As I suspected, changes to the /system directory can not be reverted by a hard reset; the /system directory is actually a YAFFS2 file system (system.img) that is supposed to be read only, and thus is not part of the recovery image. Don't play with it unless you know what you are doing. I moved some files around in /system and broke my Browser, so I performed a hard reset, but the Browser was still broken after a hard reset. Luckily I had tracked my changes and was able to revert what I had done manually.

Notes:

  • Incidentally, in the /system/bin directory there is a flash_image executable that changes the recovery.img used when you hard reset the device. I've noticed that I can run this without root access from a standard adb shell. Maybe we never needed to root the device after all... I think we can flash it without root access... I'm too scared to mess with that beyond that (I don't want to brick my phone!).
  • There is no cp command, so you must use the dd command. To get a file from your computer to your phone, copy it to the SD card, and then use the root shell to copy it from the SD card to wherever you need.
  • I've noticed that you must start up telnetd very quickly upon starting pterminal. If you wait too long, telnetd will not stay resident; it exits immediately. It looks as if there is some timing and security related issue here. I've also written a quick telnet client so I can set telnet into root on my phone, from my phone. This works fine. However, I have not been able to successfully launch telnetd from my own application (using Runtime.exec). I'm not sure what pTerminal is doing differently here.
  • Kudos to SplasPood of Xda-developers for noticing this exploit.