Kindlet Developer HowTo
The following stuff has been tested on a Kindle Touch with firmware version 5.1.0. You need to install a jailbreak before you can use this. If you're unfamiliar with UNIX and/or Linux, you probably shouldn't be doing the things described here.
NOTE: Some information in this document may be outdated, incomplete, inaccurate or even downright wrong. Please edit as you see fit.
[edit] SSH and the shell
[edit] How can I gain SSH access to my device?
SSH access is not possible until you apply a Jailbreak.
Please note that Amazon discourages this, and applying it will void warranty. However, you can't get around that if you want to develop for the Kindlet.
[edit] How do I make the home of root save its content permanently?
The home of the root user is in /tmp/root, which points to /var/tmp/root. /var is a non-permanent file system (tmpfs), which loses its contents after a restart. To make the home of root permanent, we have to move it somewhere else.
mntroot rw # If you haven't done that already cd / mkdir home mkdir home/root
This creates the new directory. We now need to tell the system to use this as our new home for root. Edit the file /etc/passwd:
cd /etc vi passwd
Change the line starting with 'root:' to the following:
root:x:0:0:root:/home/root:/bin/sh
Be sure you don't change anything else. If you've corrupted this file, I daresay you won't be able to log in again. To make sure, save your changes, quit vi, open a new terminal and open a new SSH session for root. You should now be logged in as root, and your new home should be /home/root. (If not, use your other, still open SSH session to fix the passwd file you've broken.)
NOTE: Using pubkey authentication with your ssh install could save you from some possible problems.
SECURITY: After having done the procedure above, you can now switch back to read-only mode (mntroot ro). It might be considered dangerous to leave the file system read/write all the time. If you wish more security, you could try moving the home of root to /mnt/us/root, although be warned that /mnt/us might not always be mounted, which could lead to conflicts. Probably the best thing would be, to have home of root in /home/root, mount file system read-only, create a new user whose home is under /mnt/us, and use that user whenever possible.
An alternative is to put root's home in /var/local which is always mounted and rw. Also, it is used by both 'main' and 'diags' operating systems. As in:
mkdir -p /var/local/home/root
Then make the pass word file entry match:
root:x:0:0:root:/var/local/home/root:/bin/sh
NOTE: There is a known bug when using the application adduser in conjunction with myts, when creating a new user account if you ctrl+c at the ENTER PASSWORD stage then the terminal will no longer respond to input from the keys. Please be certain of your intentions before entering that section of adduser or you may find yourself in a pickle.
RESULT OF THIS BUG: The new user is added - despite cancellation, with no password. The overall correctness of the configuration of the user account when left in this state is uncertain.
[edit] How do I use vi?
vi is a (rather old-fashioned, but still powerful) text editor. Type 'a' to start editing. Hit ESC to return to command mode. Type ':write' to save your work, and ':q' to quit vi. If you've messed up the file and don't want to save, type ':quit!'.
[edit] How can I predefine aliases, change prompt, etc. each time I log in?
These stuff are usually done in a file called ~/.bashrc. However, the Kindle's system does not support this by default. To enable it, do:
cd /etc vi profile
and add the following lines:
if [ -f ~/.bashrc ]; then source ~/.bashrc fi
Now you can write your own .bashrc.
cd vi .bashrc
Enter at least the following lines:
PS1='\e[31m\u@\h:\W> \e[0m' export PS1 source ~/.alias uname -a
This sets the prompt to red color \e[31m (indicating it's the root user), displays the user name \u, the hostname \h and the working directory \W.
To create the aliases, do
vi .alias
and add your aliases, for instance:
alias ..='cd ..' alias ls='ls -F --color' alias ll='ls -FAlsh --color' alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' alias du='du -sh' alias df='df -h'
These are my favourite aliases. They enable colour ls, activate prompt for overwrite for rm, cp and mv, and have du and df show their results in human-readable form. You might not want to use these, so change them as you wish.
[edit] How do I find a file that contains some certain text?
Do
fgrep -r "your-search-word" .
You shouldn't search directly in /, however. I tried this once, and it freezed my Kindle after some time.
If you wanted to look into the jar files that are used for Kindlet developing, you won't find that with the instruction above, because jars are zipped. Instead, copy all of the jars to your host, unzip them and search the class files.
[edit] How do I change system and hardware time?
You'll probably have noticed that time is reset each time you reboot your Kindle. The reason for this is that the settings are not written to hardware time. Open a shell and write:
date -s "2012-07-06 17:06"
or whatever time it is. This sets system time, just as happens when you use the settings menu. Now write:
hwclock -w
This sets hardware clock to system time. Time will now survive reboots.
[edit] Developing Java Kindlets
[edit] What do I need to do to start developing Kindlets?
Here's a rough sketch what you need to do:
- Apply a jailbreak on your device to gain access through SSH.
- Write a Java Applet that actually runs in a browser on your host machine (PC or Mac).
- Copy /opt/amazon/ebook/lib/*.jar as well as /opt/amazon/ebook/sdk/lib/*.jar onto your host machine, and make the path where you put them visible in your IDE (or, if you're not using any, add them to your $CLASSPATH).
- Derive your main class from com.amazon.kindle.kindlet.AbstractKindlet rather than java.applet.Applet.
- Compile your program into a jar file, using a valid manifest.
- Create appropriate developer keys in a keystore file and put it in the appropriate location on your Kindle.
- Sign your jar file with those keys and rename the jar file as *.azw2.
- Put the azw2 file into the documents folder of the Kindlet USB drive.
- Run your application.
- Have a bottle of beer (whether it worked or not.)
[edit] What should be contained in the manifest file for my Kindlet?
The manifest file should look like this:
Manifest-Version: 1.0 Main-Class: <your.package.and.Class> Implementation-Title: <Your Kindlet Title> Implementation-Version: <your version> Implementation-Vendor: <Your Name or Nickname> Extension-List: SDK SDK-Extension-Name: com.amazon.kindle.kindlet SDK-Specification-Version: 2.1 Toolbar-Mode: persistent Font-Size-Mode: point
For Toolbar-Mode, you can also say 'transient' to make the toolbar disappear until the user clicks into the upper part of the screen. With SDK 1.1, there was also an option 'none' available, however it does not seem to work any more under 2.1.
For Font-Size-Mode, there seem to be two options: "point" and "pixel".
In case of an error like "the content you are trying to open is not compatible with with your kindle", check if you have unnecessary spaces after a line.
[edit] How do I create a jar file that contains my Java classes and the manifest?
If you're compiling from the shell, use the following command to put the class files into a jar file using the manifest above:
jar cvmf <manifest> <jar-filename> <classes-directory>
Be sure the package path matches the path of the classes in the jar file. For instance, if you specified "com.me.myproject.Main" as your Main-Class in the manifest file, the argument <classes-directory> should be "com", and your class should be found in the directory com/me/myproject/Main.class.
[edit] What is needed to make my application appear on the home screen?
Create a jar file with a valid manifest, sign it with the developer keys, then rename it as *.azw2. Now move the azw2 file into the documents folder of the USB drive (or scp to /mnt/us/documents/). Your application should appear on the home screen. (A restart may be necessary.)
If you don't sign the jar file and simply rename it as *.azw2, the application would appear on the home screen, however you won't be able to launch it.
[edit] How can I get those developer keys?
Applications running on your Kindle (called Kindlets) are installed as a bundle, which is basically a signed (authorized) jar file. The reason why the bundle needs to be signed is simply, because it prevents normal users from installing their own applications.
You don't need to "steal" a keystore from somebody else, you just create your own. To create one, you use the UNIX shell command keytool. On Mac OS X and Linux, it's likely that it's already pre-installed.
A keystore holds one or more keys. Each key is given a unique alias name. The following command generates a new keystore called developer.keystore (or updates it if it already exists) and adds a single key to it. The key's alias is set to dkALIASNAME, and both the key's and the store's password are 'password'.
keytool -genkeypair -keystore developer.keystore \ -storepass password \ -keypass password \ -alias dkALIASNAME \ -dname "CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, \ C=Unknown" \ -validity 5300
You need to add three such keys, the alias of which must be a name that identifies you as a developer (such as $USER), preceded by the prefixes dk, di, and dn. For instance, if your username is simpson, add the three keys dksimpson, disimpson, and dnsimpson.
dk, di, and dn stand for kindle in general, interaction support, and network support, respectively. Although I haven't tried, I figure you won't be able to launch the Kindlet at all if you don't sign it with a dk key; you won't be able to interact with it (type, click) if you don't sign it with the di key; and you won't be able to open a network connection if you don't sign it with the dn key.
[edit] How do I install my keystore, and how do I merge it with an existing one?
Visit the Forum to get the Merged Developer Keystore.
To use your keystore, it must be placed on your Kindle:
/var/local/java/keystore/developer.keystore
You can't do this by just mounting the Kindle in USB drive mode, you need SSH access a.k.a. jailbreak. You can use scp to copy your keystore to the path above.
It's most likely there is already an existing keystore, because you've installed a jailbreak and/or kindlets from other developers. You'll need to merge your keystore with the existing one. To do this, again use the keytool:
keytool -importkeystore -srckeystore my -destkeystore developer.keystore
This merges your own keystore "my" with the existing keystore "developer.keystore".
[edit] How To install the merged developer.keystore on the Kindle
- Make sure, that you have jailbreaked your Kindle
- Make sure that you have USBNetworking installed properly on your Kindle
- Make sure, you can connect to your Kindle over SSH
- Connect your Kindle in USB-(Not-Networking!)-Mode to your Computer
- Copy the downloaded developer.keystore from the first post of the Merged Developer Keystore Forum Thread to the Kindle-USB-Drive next to the documents folder
- Safely Disconnect your Kindle
- Start USBNetworking by searching for ;un in the Kindle-Search-Bar
- Connect your Kindle and connect to it over SSH as before
- Execute these commands on your Kindle:
mntroot rw
mv /mnt/us/developer.keystore /var/local/java/keystore/developer.keystore
mntroot ro - Disconnect your Kindle
- Stop USBNetworking by searching for ;un again
- Restart your Kindle
[edit] How do I use the keys from my keystore to sign my jar file?
Use the shell command jarsigner.
jarsigner -keystore developer.keystore \ -storepass password \ My.jar dkALIASNAME
You'll need to sign your jar file with all the three keys with alias prefixes dk, di, and dn.
[edit] What are the restrictions with the Kindle's cvm?
The Kindle is running cvm as a virtual machine for Java Kindlets. So far, I've encountered the following restrictions:
- String concatenation works only with String.concat. + and += operators are not implemented. So if you have the following:
String s = a + b; s += c;
Rewrite it as:
String s = a.concat (b).concat (c);
- Math.log10 is not implemented (just returns the input number). Fortunately, Math.log is, so if you have the following:
r = Math.log10 (a);
Rewrite it as:
r = Math.log (a) / Math.log (10);
- Integer.valueOf(int_value) is not implemented; use "new Integer(int_value)" instead.
- Drawing ovals (.drawOval(IIII)V) function in Graphics) connects a line to the previous-drawn text, line, etc. Fill a (very small) oval off the screen to fix this bug immediately before drawing ovals.
- There are certainly more...
[edit] References