Tag Archives: android

Serialization JSON/Avro/Protocol buffers

Introduction

Recently I have been trying to figure out nice and effective approaches for serializing my data across different nodes. Sitting back in the rocking chair, I can recall how CORBA once upon time was supposed to be cool (it was binary and generated code I believe) but somehow was always oh so broken (only tried a few times and long ago). Then there was RMI, which I recall abusing in weird ways and ending up serializing half the app for remote calls (usually not noticing). But hey, it worked. Then came all the horrible and bloated XML hype (SOAP still makes me cringe). These days JSON is all the rage instead of XML and then there is the shift back towards effective binary representations in CORBA style but doing it a bit more right with Avro/Protocol buffers/Thrift (and probably bunch of others I never heard of).

So trying to get back into a bit more effective programming approaches, I tried some of these out for two different types of applications. One is an Android app with a backend server. Both ends written in Java. The server streams continuous location updates to a number of Android clients at one second interval. The second application is a collection of sensor nodes producing data to be processed in a “cloud” environment with a bunch of the trendy big data tools. Think sensor->kafka->storm/spark/influxdb/etc. Server side mainly Java (so far) and probe interfaces Python 3/Java (so far).

For the Android app, I hosted the server in Amazon EC2 and wanted to make everything as resource efficient as possible to keep the expenses cheap. For the sensor/big data platform the efficiency is required due to the volume etc. (I think there is supposed to be X number of V’s there but I am just going to ignore that, arrr..). So my experiences:

JSON:
+No need for explicit schema, very flexible to pass just about anything around in it
+Works great for heterogeneous sensor data
+Very easy to read, meaning development and debugging is easy
+Can be easily generated without any special frameworks, just add a bunch of strings together and you are done
+Extensive platform support
+Good documentation as it is so pervasive. At least for the format itself, frameworks might vary..

-Takes a lot of space as everything is a string and tag names, etc., are always repeated
-String parsing is always less efficient in performance as well than custom binaries

Avro:
+Supported on multiple platforms. Works for me both on Python3 and Java. Others platforms seem extensively supported but what do I know haven’t tried them. Anyway, works for me..
+Effective binary packaging format. Define a schema, generate code that creates and parses those binaries for you.
+Documentation is decent, even if not great. Mostly good enough for me though.
+Quite responsive mailing lists for help
+Support reflection, so I could write a generic parser to take any schema with a general structure of “header” containing a set of metadata fields and “body” a set of values, and, for example, store those in InfluxDB (or any other data store) without having to separately write deserializers for each. This seems to be also one of the design goals so it is well documented and support out-of-the-box.

-The schema definition language is JSON, which is horrible looking, overly verbose and very error prone.
-The generated Java code API is a bit verbose as well, and it generates separate classes for inner “nested records”. For example, consider that I have a data type (record) called TemperatureMeasure, with an inner fields called “header” and “body” which are object types themselves (called “records” in Avro). Avro then generates separate classes for Header and Body. Annoying when having multiple data types, each with a nested Header and Body element. Need to come up with different names for each Header and Body (e.g., TemperatureHeader), or they will not work in the same namespace.
-The Python API is even more weird (or I just don’t know how to do it properly). Had to write the objects almost in a JSON format but not quite. Trying to get that right by manually writing text in a format that some unknown compiler expects somewhere is no fun.
-As a typical Apache project (or enterprise Java software in general) has a load of dependencies in the jar files (and their transitive dependencies). I gave up trying to deploy Avro as a serialization scheme for my Android app as the Android compiler kept complaining about yet another missing jar dependency all the time. Also dont need the bloat there.

Protocol buffers:
+Effective binary packaging format. Define a schema, generate code that creates and parses those binaries for you. DeJaVu.
+Documentation is very good, clear examples for the basics which is pretty much everything there is to do with this type of a framework (generate code and use it for serializing/deserializing)
+The schema definition language is customized for the purpose and much clearer than the Avro JSON mess. I also prefer the explicit control over the data structures. Makes me feel all warm and fuzzy, knowing exactly what is is going to do for me.
+Only one dependency on a Google jar. No transitive dependencies. Deploys nicely on Android with no fuss and no problems.
+Very clear Java API for creating, serializing and deserializing objects. Also keeps everything in a single generated class (for Java) so much easier to manage those headers and bodies..
+The Python API also seems much more explicit and clear than the Avro JSON style manual text I ended up writing.

-Does not support Python 3, I suppose they only use Python2.7 at Google. Makes sense to consolidate on a platform, I am sure, but does not help me here.
-No reflection support. The docs say Google never saw any need for it. Well OK, but does not work for me. These is some support mentioned under special techniques but that part is poorly documented and after a little while of trying I gave up and just used Avro for that.

Conclusions
So that was my experience there. There are posts comparing the effectiveness in how compact the stuff is etc between Avro, Protocol buffers, Thrift and so on. To me they all seemed good enough. And yes, Thrift I did not try for this exercise. Why not? Because it is poorly documented (or that was my experience) and I had what I needed in Avro + Protocol buffers. Thrift is intended to work as a full RPC stack and not just serialization so it seems to be documented that way. I just wanted the serialization part. Which, from the articles and comparisons I could find, Thrift seemed to have an even nicer schema definition language the Protocol buffers, and should have some decent platform support. But, hey if I can’t easily figure out from the docs how to do it, I will just go elsewhere (cause I am lazy in that way…).

In the end, I picked Avro for the sensor data platform and Protocol Buffers for the Android app. If PB had support for reflection and Python 3 I would much rather have picked that but it was a no go there.

Finally, something to note is that there are all sorts of weird concepts out there I ran into trying these things. I needed to stream the sensor data over Kafka and as Avro is advertised as “schemaless” or something like that, I though that would be great. Just stick the data in the pipe and it magically parses back on the other end. Well, of course no magic like that exists, so either every message needs to contain the schema or the schema needs to be identified on the other end somehow. The schema evolution stuff seems to refer to the way Avro stores the schema in files with a large set of adjoining records. There it makes sense as it is only stored once with a large set of records. But it makes no sense to send the schema with every message in a stream. So I ended up prefixing every Kafka message with the schema id and using a “schema repository” at the other end to fix the format (just a hashmap really). But I did this with both Avro and PB so no difference there. However, initially I though there was going to be some impressive magic there when reading the adverts for it. As usual, I was wrong..

Sending SOAP messages in Java without any libraries

Needed to send some SOAP requests in Android. There was no nice library available to do it all. Not that I would care for them much, SOAP is bloated, Java libraries are typically bloated, and combining the two probably gets obese. Anyway, how can I send SOAP messages with only the most basic methods?

After a bunch of experiments, here is one just using plain sockets:

  public static void send(String hostname, String uri, String msg) throws Exception {
    int port = 80;

    String authString = "username:password";
    byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
    String authStringEnc = new String(authEncBytes);

    StringWriter sw = new StringWriter();

    Socket socket = new Socket(hostname, port);
    PrintWriter pw = new PrintWriter(sw);
    PrintWriter pw2 = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
    pw.print("POST "+uri+" HTTP/1.1");
    pw.print("\r\n");
    pw.print("Host: " + hostname);
    pw.print("\r\n");
//    pw.print("Content-Type: application/soap+xml; charset=utf-8");
    pw.print("Content-Type: text/xml");
    pw.print("\r\n");
    pw.print("Authorization: Basic " + authStringEnc);
    pw.print("\r\n");
    int length = msg.getBytes().length;
    pw.print("Content-Length: " + length);
    pw.print("\r\n");
    pw.print("\r\n");
    pw.print(msg);
    pw.print("\r\n");
    pw.flush();

    String all = sw.getBuffer().toString();
    System.out.println("SENDING:");
    System.out.println(all);
    pw2.print(all);
    pw2.flush();

    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    for (String line ; (line = reader.readLine()) != null ; ) {
      System.out.println(line);
    }
    reader.close();
    pw.close();
    socket.close();
  }

Why all the print(“\r\n”) and not println()? because I was going nuts trying to figure out what was the problem and tried to ensure no linefeed conversion problems would be there..

Problem: At first I looked up the examples from W3C SOAP tutorials/examples. There the content-type was set to “application/soap+xml” as shown in the commented line in the example above. This kept producing “500 internal server error”, with “soap-fault” mentioned in the header. Was confusing since there seemed to be no other explanation. I had copy-pasted the response read code from somewhere and it stopped after headers.. Fixed in the above now.

Anyway, after installing Wireshark (what a pain on OSX..), I finally figured it out. There was also an error string in the response body “Transport level information does not match with SOAP Message namespace URI”. The problem is that the namespace in my SOAP message was for SOAP 1.1, and the content-type “application/soap+xml” is for SOAP 1.2. And in some combination the SOAPAction header would be required as well.. The answer? Change the content-type to “text/xml”, which makes it all match SOAP 1.1 specs. Whooppee.

Some good diffs at http://www.cnblogs.com/wangpei/p/3937541.html.

The socket version is handy to print out exactly what you are sending and to debug if the error is in some configuration of the libraries used or just in the data being sent. Since the socket version uses no libraries, it should rule that out..

Of course, running this on Android might as well use the Apache HttpClient that comes with it. Version using Apache HttpClient v4.4.1 on a desktop (yesyes port it to Android later..):

  public static void sendHTTPClient(String url, String body) throws Exception {
    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpPost post = new HttpPost(url);

    String authString = "username:password";
    byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes());
    String authStringEnc = new String(authEncBytes);

    StringEntity strEntity = new StringEntity(body, "UTF-8");
    strEntity.setContentType("text/xml");
    post.setHeader("Authorization", "Basic " + authStringEnc);
    post.setEntity(strEntity);

    HttpResponse response = httpclient.execute(post);
    HttpEntity respEntity = response.getEntity();

    System.out.println("Response:");
    System.out.println(EntityUtils.toString(respEntity));
  }

Find my lost Android app signing key from IntelliJ

Over time I seemed to have lost my keystore password, that I needed to sign some Android app. Still had the keystore file though. IntelliJ also had the keystore password stored. So how do I get the password from IntelliJ?

There is a handy utility on github for this https://github.com/corneliudascalu/intellij-decrypt. If you have the master password for IntelliJ, it decrypts all passwords stored in IntelliJ. Using this I was able to recover the signing password for my keystore.

This requires the security.xml file from IntelliJ where the passwords are stored. Where is this stored on OSX? Googleing for it, this seems to vary. None of the given options seem to make an sense. So we try “find / -type f -name security.xml”. This finds it in “/Users/myname/Library/Preferences/IdeaIC14/options/security.xml”. That’s how I finally got the keystore password.

Android updating preloaded database

So.. to use a preloaded database in our app, we have to put it in our assets (or similar) and copy it over to the correct directory. But how do we update it when the contents change and we do an update of the app for someone who has the old version?

There is the class that extends SQLiteOpenHelper and is used to create and update the database. It has a method called onUpgrade(…) that is to be called by the Android OS whenever the database version changes. And the database version is considered to have been changed when the database stored on the device has a different version number than what we give in the constructor of the class we extend from SQLiteOpenHelper.

The obvious solution is to delete the old DB file when onUpgrade() is called and copy the new version over. Too bad it does not work. You get an error something like this:

Caused by: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)

This happens after you have replaced the file in onUpgrade() and next time you try to get a database reference with getWritableDatabase() or getReadableDatabase(). It only happens the first time, so once the app has crashed on doing the update, it will actually work when started the next time.

Reason: I assume the Android sqlite implementation opens the database file when calling onUpgrade() and assumes it is the same file forever after that. This is hinted at by the following log entries:

SQLiteLog﹕ (1032) statement aborts at 4: [PRAGMA user_version = 2]
SQLiteLog﹕ (28) file unlinked while open: /data/data/my.package/databases/my_db.db

Looking around the net for “sqlite unlinked file” finds some sqlite documentation also mentioning this possibility. This is the latter of the above lines. The former seems to indicate this happens when the sqlite implementation tries to upgrade the database version with the command “PRAGMA user_version = 2” after doing the onUpgrade() call (where we deleted the file it tries to change version on now).

The solution? Never change the DB version number given to sqlite. I instead stored it as part of user preferences on the device. Since copying even a large database file takes maybe less than a second for me, I could always copy it and forget the upgrade check. But I figured it wouldn’t be nice on the users filesystem and also could have random issues if the disk/memory is too full later. So I keep my own versioning in user preferences.

Which brings me to the last point. I prepared a large file taking too many hours trying to get it right and to upload it to the device. This then had the db file version number 2. If I upload it to the device, the system is not always too happy about having previously had a version 1 database and suddenly getting shoved a version 2 database while claiming to the SQLiteOpenHelper class that it is a version 1 file (to avoid the doUpgrade call).

So how do we change the version of an existing database file? The solution is the one in one of the above errors. Load up the database file in an sqlite client (I used the Firefox extension) and run the SQL command “PRAGMA version;” to get the version it is currently at. Then run the SQL command “PRAGMA version = 1;” to set it to version 1. Now uploading this works just fine, Android sqlite does not try to upgrade, and the app upgrades work with the preferences trick…

Android Google+ sign in and Game services

Wanted to try out the Google Game Services for Android. Well, I guess there are more than just Game Services since they include Google+ in general, Google Drive, etc…

So to use them I need to get the user to sign in to their Google+ account. Can’t be too hard, can it? I mean basically to set up the device you need to log in using a Google account. So you should have a Google account if you get here, and pretty much be logged in already as mostly using the device requires this. Unless you wear a tin foil hat, rooted your phone, and declined to install any Google services (so much fun on Android without them.. I tried it :)).

Well not so simple. Here is what worked for me finally. This is at this time on Android Lollipop, a.k.a. Android 5.0.

The documentation tells me to do something like this:

...
private GoogleApiClient client;
...
client = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN)
.addApi(Games.API).addScope(Games.SCOPE_GAMES)
.build();
client.connect();

To me this seems to say, I want to use Google+ sign in service and the Game services, connect to the servers to set up the API access. But it does not seem to indicate anything about signing in anywhere. Then what? How do I make the user sign in? The docs just show this and leave me to it. To me that is what it seems like anyway.

Well, apparently the “client.connect()” invocation tries to make the user sign in. But typically the user is not signed in (for the app) when they start to use the app (meaning you are not allowed to access their Google info and services). So then what happens? I have to define the listener interfaces given to the builder above as in “addConnectionCallbacks()” and “addConnectionFailedListener()”. Something like:

...
  private boolean boolean resolving = false;
...
  @Override
  public void onConnected(Bundle bundle) {
    //if we ever get here, we can actually use the Google services we asked for whoppee
    String achId = getString(R.string.achievement1);
    Games.Achievements.unlock(client, achId);
  }

  @Override
  public void onConnectionSuspended(int i) {
    //retry to give user error to resolve
    client.connect();
  }

  @Override
  public void onConnectionFailed(ConnectionResult result) {
    if (resolving) return;
    resolving = true;
    if (result.hasResolution()) {
      try {
        IntentSender sender = result.getResolution().getIntentSender();
        startIntentSenderForResult(sender, RC_SIGN_IN, null, 0, 0, 0);
      } catch (IntentSender.SendIntentException e) {
        // The intent was canceled before it was sent.  Return to the default
        // state and attempt to connect to get an updated ConnectionResult.
        client.connect();
      }
    } else {
      // not resolvable... so show an error message
      int errorCode = result.getErrorCode();
      Dialog dialog = GooglePlayServicesUtil.getErrorDialog(errorCode, this, RC_SIGN_IN);
      if (dialog != null) {
        dialog.show();
      } else {
        // no built-in dialog: show the fallback error message
        showAlert(this, "Error in signing in.");
      }
    }
  }
...

So what does all this mean? When we call the “connect()” method on the GoogleApiClient, if the user has not previously used the app and signed in for it, the “onConnectionFailed” is called. This has the “hasResolution” flag set if the user can log in (why couldn’t they? I dunno maybe some service is not available?).

If you then start the “IntentSender” attached to the response, it opens up the actual Google+ sign-in dialog. Using this the user can finally sign in to enable the Google services for the app.

Then there are all the weird error conditions and their handling. If the sign-in error has no resolution, there might be some way to fix it by user action, such as installing Google Play services. Getting the “GooglePlayServicesUtil.getErrorDialog()” gives you the dialog that enables the user to perform this action, if any are available. It also enables shows the user a message telling what went wrong. But hey, it gets better, this dialog is not always available, in which case you should create your own generic dialog to tell the user “something went wrong” (the “showAlert()” above).

Of course, you have to track if the resolving action is already going on and this just happens to be called again and this is what the “resolving” flag is for. Wonderfully complicated, no? Well luckily it is not this simple.

Using the PendingIntent to show the sign-in dialog does not actually end up with the user signed in to your app. It just enables you to connect while the user has accepted your app for access to their Google services. So we need to listen to the events from the sign in dialog. This needs:

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RC_SIGN_IN) {
      resolving = false;
      if (resultCode == Activity.RESULT_OK) {
        // Make sure the app is not already connected or attempting to connect
        if (!client.isConnecting() && !client.isConnected()) {
          client.connect();
        }
      }
    }
  }

This gets called after the user had “interacted” with the sign in dialog. So what is the RC_SIGN_IN value? Anything you like (I think it was 9001 and 0 in different examples). This is an identifier we gave earlier in the “startIntent..” call higher above to identify what activity just arrived here. Activity.RESULT_OK is a value that indicates the user chose “OK” for the sign in dialog and we now have the permission. Then (if we are not already doing so…) we try to do “GoogleApiClient.connect()” again. Why, because now we should have the permission to do so and now the connection should happen successfully (as in signing in and enabling the Google services).

But is this all? Well of course it cannot be so simple. You can get all sorts of resultCode values. Not just from Activity.XXX but also, for example, from GamesActivityResultCodes.XXX. I got 10002, which refers to an error in signing in. Sweet. Which can be if you have not correctly set up the application id in the Google Developer console. But there is more. It can also be if you have not published your app but are developing it using a Google ID that is not the owner of the application id on the Developer console. Then you must add it to the list of allowed test accounts for the application under the Developer console (Testing/Add Testers). After this it finally worked.

Few extra notes.

All these errors can be really cryptic as it might seem that nothing is happening when any of the callbacks are not called, and no error is visible. Yet nothing happens. Well, to see the errors one has to remove all filters from logcat and try again. At least on IntelliJ, the IDE by default filters non-app related stuff out. So good luck debugging that if you dont happen to notice this.

Some of the Google example also show using a Fragment to show the error dialog. Somewhere in the GameUtils classes they host (at this time) on github (hey, they dont like Google code?) I found something along these lines:

  /**
   * Show an {@link android.app.AlertDialog} with an 'OK' button and a message.
   *
   * @param activity the Activity in which the Dialog should be displayed.
   * @param message the message to display in the Dialog.
   */
  public static void showAlert(Activity activity, String message) {
    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    builder.setMessage(message)
            .setNeutralButton(android.R.string.ok, null)
            .create()
            .show();
  }

And finally, it seems that if there is an issue, the “onConnectionSuspended()” gets called and you can then use the “connect()” method again to show some error dialog (by triggering the onConnectionFailed() method..).

And once the user has signed in once, the initial connect() seems to work and they are not automatically signed in without all the extra methods etc. Whee.

Just to note, the stuff in these examples is inside the main Activity class. Otherwise some of the method calls and overrides will not work. Otherwise you can implement if in other ways of course..

Somehow I was hoping for something like “GooglePlus.signIn()”. Would that be so hard? Well, just hoping this works now.. I am sure I still got plenty of things wrong. But lets see.

Accessing Android databases from device

(I thought I wrote this down..)
Problem: how to access database on my Android device?

solution:
open command prompt
cd to ANDROID_SDK_HOME
cd platform-tools
./adb shell
run-as applicantion.package.from.androidmanifest.xml
cd databases
ls
##and here is the list of your databases

now, if we want to copy this to development computer, we do
cp mydatabasefile.db ..
cd ..
chmod 777 mydatabasefile.db
exit
cd /data/data/yourpackagename
cp mydatabasefile.db /sdcard
exit
exit

now you should be back in your pc shell, not in adb anymore

./adb pull /sdcard/mydatabasefile.db

now the file should be on your compewter

and finally, remove the file from sdcard

./adb shell
cd /sdcard
rm mydatabasefile.db

oh well, i used to be able to directly download it from the data folder after the chmod 777. maybe the 5.0 updated changed it or i just forgot.. need to document this stuff sooner lest i forget.

probably the extra copy before sdcard is not even required. and this editor eats all tags with any html like symbols.. whatever..

setting up robotium rc for an apk in google play

goal: want to test an app available as apk from google play.

install app to test on the device
install android sdk and configure it to connect to your device (which is in usb debug mode etc)
install apk extractor from google play

open apk extractor
have it extract the apk for you app of interest
do “adb shell” on your windows machine from command prompt
in shell “cd sdcard” “cd ApkExtractor” “ls”
you now have the name and path to the extracted apk file
exit adb shell (just “exit” will do)

now you are back in your windows pc shell (command prompt)
now you need the path and filename of the apk you extracted and checked above
in windows command prompt execute “adb pull PATH_TO_FILE_ABOVE”
the extracted apk file is now in the directory on your windows pc whereever you executed the pull command

now, to use the apk file for testing with robotium, it must be re-signed with your debug key. for instructions see here: https://code.google.com/p/robotium/wiki/RobotiumForAPKFiles. basically you download a re-signing application, run the apk through it to strip the signatures and to re-sign it with your debug key. if all is installed correctly it is like magic and no need to do anything but drag and drop the apk into the signer.

notice that the re-signer should tell you what package the app has and what is the main activity to start it. these are useful information to have when getting robotium up and running.
also, if you need to check the manifest.xml in the APK for some activity information etc I recommend looking for some form of apk extractor of android manifest extractor as there are some tools that give everything in there as readable, by default they are a bit “obfuscated” in the apk file (which opens fine with winzip etc)

now for running robotium rc to test your app, you naturally need to install robotium rc (doh):https://code.google.com/p/robotium/wiki/RemoteControl

to run your newly signed app use the following jre arguments: aut=PATH_TO_YOUR_NEW_DEBUG_APK messenger=C:\robotiumrc\SAFSTCPMessenger\bin\SAFSTCPMessenger-debug.apk runner=C:\robotiumrc\SAFSTestRunner\bin\SAFSTestRunner-debug.apk instrument=com.jayway.android.robotium.remotecontrol.client/com.jayway.android.robotium.remotecontrol.client.RobotiumTestRunner

that is assuming the normal robotiumrc install dir.

note that robotium rc will upload a fresh copy of the apk at every run with this. the first time you do this you may get an error about invalid or inconsistent certificates. manually uninstall the original apk from the device and have robotium re-install the new resigned apk and it should be fine.

setting up android uiautomator

create the ant build file:

C:\>android create uitest-project -n myprojectname -t 1 -p c:\code\myproject

build the test code:

cd c:\code\myproject

ant build

push (upload) it to the device:

C:\code\myproject>adb push c:\code\myproject\uitest\bin\myprojectname.jar /data/local/tmp/

run the test:

C:\code\myproject>adb shell uiautomator runtest myproject.jar -c example.Test

have to have junit3 jar file to write the code. not sure if the generated build file references this somewhere in the android dev kit or if it can pick it up from my project files. had it in the libs dir. but finding it was a bit hard as the only thing available seems to be junit4 not. but found it in the maven central repo.

nice thing seems to be that you can run almost any code as part of this special type of test case uploaded. no need to fiddle with permissions or anything. also logging works fine and system.out seems to print on the pc console? great, although i might be just mistaken..

installing jelly beans on galaxy tab2 7

needed to use the latest android to get access to some developer tools. only had a development tablet galaxy tab2 7.0 (3110 model). as usual, official updates from samsung are hopeless. so off to try cyanogenmod that everyone says is so easy, nice, great and everyone should root their stuff cause of all the nonsense you can then install. so sure, how hard can it be..

first of all, just forget about transferring any data from the old apps to the new OS version. root is needed for any reasonable backup programs and manually guessing directory names is just hopeless. and to root just as well as to update the os bunch of stuff needs to be guessed. here is the correct sequence of actions (for me it was, for anyone else this is likely the same kind of gibberish as everyone elses post was for me):

download clockworksmod, this is what every website refers to when they say CWM. how hard would that be to tell? and dont even hope to find it on some site like the clockworksmod.com as that is obviously hopeless. instead, rely on some random links on the internet, half of which do not work and half of which point to some alien sites that could just as well root your tablet with malware of all kinds. nice.

now there is a good chance the clockworksmod file is an “.img” file. and then they will tell to use some tool called heimdall. which will give errors such as libusb 12 error or claiming interface failed. and install it with samsung usb this and that, which is obviously not on the heimdall list. and so on. oh but there is a tool called odin, which is supposedly from samsung but can only be downloaded again from some random sites giving a good indication of installing a bunch of malware. but what are your options? none, thank you. but odin is the tool i used after downloading from somewhere odd.

so we try odin.. and it requires a .tar file and will not take a .img file. how nice. and all sites tell you to get the tar file from somewhere. yeah sure, rooting the tab or installing cyanogenmod just seems like such a lovely idea. if only i had gotten the nexus thing.. so off to google for some filename with the .tar in the end for the clockworks. this we then install with odin. and we need to put it in the PDA slot thank you very much. many sites talk about choosing the PIT as some device or OS type. which opens a file dialog, thank you very much, makes no sense.

and you need to put your tab in a “download” mode. which should be done by booting it up while pressing power+volume down. but nothing happens. of course you must remove the USB cable first which no-one tells you. so we get there, now what? now we run odin and how do we know odin is happy? there is a bunch of small rectangles in odin, and in one of them some text like COMx appears. thats it. whee. then we load the .tar file into the PDA slot and hit start. all seems to go great.

but wait, we boot the device up and we need to make it enter something called “recovery mode”. thats what all the sites tell you, now boot it up in recovery mode. so, of course if we just reboot the device it goes into this mode right? not. and lets not forget that the sites tell you to leave the odin settings on to reboot the device after and all that. so the reboot takes you back to the old OS and nothing seems to have changed. great.

what then? we reboot and somehow find out that to enter recovery mode we must hit power+volume UP now. ooh. but wait, it loads a recovery mode but there is a problem. how do we get it to install the cyanogenmod OS now? well it appears we must load the cyanogenmod as a zip file, put it on a microSD card, and we should install it from there. so we get a microSD card, download the correct version of cyanogenmod (of course only nightly builds are available, how reassuring) and put it on the card. insert card into tab. now what? not we boot into the recovery mode we discovered. then we choose install from external storage (the menu moves with volume key and power key selects, gee i though it would power the device off..). of course it complains that the zip is not signed. what is wrong?

well what is wrong is that the recovery mode is not actually clockworksmod but the original samsung recovery mode. so what happened to the clockworksmod that we thought we installed? apparently if you dont boot straight into it after installing it the tab overwrites it or something like that. and if you leave the odin settings as was instructed it boots right away and you have no time to invoke the “recovery mode” once you figure out what that is. whooppee.

so we disable autoboot from odin and reflash that in the download mode that we need to reboot to after having booted the device for the 100th time again. once the flash is done, we then manually boot it straight into clockworks. this time the recovery mode actually says clockworks and this allows us to install the unsigned zip file from the sd card. whee now it will work for sure.. or not.

so we boot and it gets stuck on cyanogenmod logo showing and some cycle turning around it while the picture size is distorted. and there it hangs forever. solution? you need to go back to clockworks and do “factory reset” to wipe data. here i though that would get the actual original rom back. no, it is needed. as is wiping the cache on the main menu and again in the advanced menu for dalvik. and after this it boots into the same stuff. ooh.

but if we wait long enough now the circle turns the right size and finally it boots into the vanilla, boring and pretty ugly standard android with nothing special in it. oh yeah and with nothing special i mean no gmail, no google play, none of that. so how do you get any apps or whatever? well this is what all the sites refer to when they say “install clockworksmod (or CWM as they would say), cyanogenmod (or some weird acronym for that), and gapps”. yea the “gapps” is google apps. this includes google play, gmail and all that.

so off to google for that and download it. put the zip of the sd card again and install by booting to clockworks again. luckily this time at least clockworks was still there. and after booting again we get all the usual android install gimmicks with google play and all that. finally.

and now it starts to download all your apps from the google play store, you just lost all the data for them but it tries to install them again without the data. that seems to work, whooppee. it seems i am also rooted after this. so now i could backup my app data, too bad it is gone already.

so, all in all very easy and simple right. why doesn’t everyone root their stuff and install cyanogenmod? after all it is so simple, my mom could do it. yeah, absolutely.