I'll use this diary entry to collect known facts about Nokia 7650 mobile phone and corresponding technologies (and I will update it when new facts come to my attention).
Nokia 7650 is the member of so called Series 60 developer platform. Devices in this series are based on Symbian OS and support both C++ and Java applications. 7650 contains Symbian OS 6.1.
Band Functionality: GSM900/GSM1800 |
Browser Version: WAP 1.2.1 |
Java APIs: CLDC 1.0 (JSR 30), MIDP 1.0 (JSR 37), Nokia UI API |
Data Bearers: HSCSD, GPRS |
PC Connectivity: Bluetooth, Infrared |
Display resolution: 176 x 208 |
Color Depth: 4096 Colors (12 bit) |
Heap Size: free RAM (up to 1.4MB) |
Shared Memory for Storage: free user memory, up to 4 MB |
Maximal Jar File Size: up to 4MB, WAP GW restrictions applies |
Messaging: SMS, MMS, Email |
Music Support: MIDI tones (poly 24), AMR audio clips (NB-AMR) |
Dimensions: 114 x 56 x 26 mm |
Weight: 154 g |
Navigation Key: Grid key mat, 2 labeled soft keys, 5 way scrolling |
Extra Features: VGA camera (640x480), SMTP/POP3/IMAP4 email, Handsfree speaker |
*#0000#
) as:
V 5.06 02-06-03 NHL-2NANice introduction to Series 60 is the document Developer Platform 2.0 for Series 60: Introductory White Paper (Dev_Plat_WP_v_1_0.pdf, 2.88 MB). Although primary oriented to business customers, it also describes differences between Developer Platform 1.0 and 2.0.
Getting Started with Java(TM) Technology (Getting_Started_with_Java_Technology_v1_1.pdf, 387 kB) is a very good introductory document for people who never used GUI programming environments. It also contains step-by-step creation of HelloWorld project.
Introduction to Series 60 Applications for Java Developers (Introduction_to_Series_60_Appls_for_Java_Developers.pdf, 122 kB) is much better introduction for developers. It contains the description of Java implementation for Series 60 devices.
Java 2 Platform, Mobile Edition (J2ME) consists of two main components:
javax.microedition.midlet.MIDlet
class. This class controls the life cycle of
each MIDlet by those three methods: startApp()
, pauseApp()
and destroyApp()
.
MIDlet suite is a JAR (Java ARchive) file containing several MIDlets and other classes. MIDlet suite is described by small text file called Java Application Descriptor (JAD file) that contains e.g. the name of the provider of MIDlet suite, the name of MIDlet suite, its size and URL.
The following table shows the source code of HelloWorld
midlet:
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class HelloWorld extends MIDlet implements CommandListener { private Command exitCommand; public HelloWorld() {} public void startApp() { Displayable current = Display.getDisplay(this).getCurrent(); if(current == null) { TextBox helloScreen = new TextBox("Hello World!", "Hello World!", 256, 0); exitCommand = new Command("Exit", Command.EXIT, 1); helloScreen.addCommand(exitCommand); helloScreen.setCommandListener(this); Display.getDisplay(this).setCurrent(helloScreen); } } public void pauseApp() {} public void destroyApp(boolean b) {} public void commandAction(Command c, Displayable d) { if (c == exitCommand) { destroyApp(false); notifyDestroyed(); } } } |
Nokia Developers Suite for J2ME | Sun J2ME Wireless Toolkit 2.1 | Sun J2ME Wireless Toolkit 1.0.4 | Real device |
MIDlet-1: HelloWorld, HelloWorld.png, HelloWorld MIDlet-Jar-Size: 1169 MIDlet-Jar-URL: HelloWorld.jar MIDlet-Name: HelloWorld MIDlet-Vendor: Pavel Janik MIDlet-Version: 1.0How to deploy the MIDlet JAR file to the device? There are several ways to do that.
Sun J2ME Wireless Toolkit 2.1 provides the capability to run MIDlet via OTA. You will enter the URL of the HTML page containing link to JAD file and J2ME WTK simulates the phone fetching the file from the network, asking the user if he really wants to install the application and will finally install it. By default J2ME WTK uses webserver on the localhost so you can test it even without connection to the network. This is good for testing, but it is useless for deployment to real device.
How it works?
1. Intro screen of Sun J2ME WTK 2.1 when running via OTA (Over-the-Air). | 2. The Application Management System (AMS) page allows you to run the installed applications (none is installed by default) or to install new applications. |
3. When installing the application, you must enter the URL to the page
containing the link to JAD file. Sun J2ME WTK 2.1 automaticall creates HTML page for you in
the bin/ directory. |
4. When the page is displayed, you can chose which JAD file to open. |
5. JAD file is displayed. The name of the application, its size, version, vendor and the full location of it JAR file are displayed. You can choose to cancel the installation or to install it. | 6. Application was successfully installed and is now listed on the main AMS page. |
The MIME type for JAD files is text/vnd.sun.j2me.app-descriptor
, the MIME type for JAR
files is application/java-archive
.
There are also other ways to deploy the MIDlet to the real device: via infrared (look at the OpenOBEX project), via Bluetooth, via e-mail and via MMS.
Going the systematic way of learning new things, I started to read CLDC specification. Here are my notes.
Connected, Limited Device Configuration, specification version 1.0a, Java 2 Platform Micro Edition
This specification defines Java platform for small devices (from only 160 kB to 512 kB of memory for Java virtual machine, configuration libraries, profile libraries and applications). CLDC is the core definition of Java platform and is the basis for profiles. Profiles are more focused definitions of Java platform for particular devices.
CLDC specification addresses the following areas:
java.lang.*
, java.util.*
)So the architecture of CLDC devices is layered. The lowest level is host operating system (Symbian OS 6.1 in case of Nokia 7650), Java virtual machine, CLDC libraries (Java libraries), profiles (Java libraries, MIDP profile).
Java application is run by JVM by calling one single main
function declared as
public static void main (String[] args)CLDC specification does not support floating point numbers, finalization of class instances.
There are two types of class libraries in CLDC:
java.lang.*
(Object
, Class
, Runtime
, System
, Thread
,
interface Runnable
, String
, StringBuffer
, Throwable
,
Boolean
, Byte
, Short
, Integer
, Long
,
Character
, Math
), java.util.*
(Vector
, Stack
,
Hashtable
, interface Enumeration
, small subset
of Calendar
, Date
and TimeZone
, Random
)
and java.io.*
(InputStream
, OutputStream
, ByteArrayInputStream
,
ByteArrayOutputStream
, interface DataInput
, interface DataOutput
,
DataInputStream
, DataOutputStream
, Reader
, Writer
,
InputStreamReader
, OutputStreamWriter
, PrintStream
). CLDC
specific classes are contained in the package javax.microedition.*
(io
).
Properties in CLDC: java.util.Properties
is not implemented, but several
properties are defined:
microedition.platform
- name of the host platform or device
(default null
)microedition.encoding
- default character encoding
(default ISO8859_1
)microedition.configuration
- name and version of the supported configuration
(default CLDC-1.0
)microedition.profiles
- names of supported profiles separated by blanks
(default null
)System.getProperty(String
property)
. Profiles may define additional properties.
Properties in NDS for the Java 2 Platform, Micro Edition's emulator (Series 60 MIDP Concept SDK Beta 0.3.1, Nokia Edition):
microedition.platform = j2me microedition.encoding = ISO-8859-1 microedition.configuration = CLDC-1.0 microedition.profiles = MIDP-1.0 MIDP-2.0Properties in Sun's Java 2 Platform, Micro Edition Wireless Toolkit's emulator (DefaultColorPhone):
microedition.platform = j2me microedition.encoding = ISO8859_1 microedition.configuration = CLDC-1.1 microedition.profiles = MIDP-2.0Properties in real Nokia 7650:
microedition.platform = Nokia7650 microedition.encoding = ISO8859_1 microedition.configuration = CLDC-1.0 microedition.profiles = MIDP-1.0Generic Connection framework defined in CLDL allows network/serial connections. CLDC spec doesn't define protocols, only the framework. It is profile's task to define supported protocols.
The second basic specs is J2ME Mobile Information Device Profile (MIDP), specification version 1.0a, Java 2 Platform Micro Edition. Profile of Java 2 Micro Edition is a device-type specific set of APIs.
The MIDP specification defined minimal hardware characteristics as: screen resolution at least 96x54, 1bit display depth, pixel shape approximately 1:1, at least one input device (ITU-T phone keypad, QWERTY keyboard or touchscreen). Minimal memory requirements are: 128 kB non-volatile memory for MIDP components, 8 kB non-volatile memory for persistent data storage and at least 32 kB volatile memory for Java runtime (heap). And of course: two-way networking with limited bandwidth.
MIDP defines additional properties:
microedition.locale
- current locale of the device
(default null
)microedition.profiles
- defined in CLDC, must contain at
least MIDP-1.0
locale
to contain both language and country
(like cs-CZ
), but Nokia 7650 returns only en
or de
according
to the selected language of the GUI.
You can use java.lang.Class.getResourceAsStream
for access to resource files bundled in
MIDlet's JAR file. This code reads the contents of the resource file resourcefile.txt
and displays it in the TextBox.
import java.io.*; import java.lang.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ApplicationResources extends MIDlet implements CommandListener { public ApplicationResources() {} public void startApp() { Displayable current = Display.getDisplay(this).getCurrent(); String resValue; if(current == null) { InputStream resFile = getClass().getResourceAsStream("resourcefile.txt"); byte[] resByteArray = new byte[100]; int resLength = 0; try { resLength=resFile.read(resByteArray, 0, 100); } catch (java.io.IOException ex) { notifyDestroyed(); } resValue = new String(resByteArray, 0, resLength); TextBox resourcesScreen = new TextBox("Resource contained in JAR file", resValue, resLength, 0); Display.getDisplay(this).setCurrent(resourcesScreen); } } public void pauseApp() {} public void destroyApp(boolean b) {} public void commandAction(Command c, Displayable d) {} } |
java.lang.System.exit
must throw java.lang.SecurityException
(MIDlets should exit only by MIDlet.notifyDestroyed
). The same applies
to java.lang.Runtime.exit
. When you call System.exit
in the emulator, you
get:
startApp threw an Exception java.lang.SecurityException: MIDP lifecycle does not support system exit. at java.lang.Runtime.exit(+9) at java.lang.System.exit(+7) at Exit.startApp(+4) at javax.microedition.midlet.MIDletProxy.startApp(+7) at com.sun.midp.midlet.Scheduler.schedule(+266) at com.sun.midp.main.Main.runLocalClass(+28) at com.sun.midp.main.Main.main(+116)Real device will produce alert "Application Error. Exiting...".
Timer and TimerTask classes. This part of MIDP specification allows applications to set alarms and be notified when they expire. Timers can be set to expiry once and also expire repeatedly. It is implemented by two classes:
java.util.TimerTask
- this class implements interface Runnable
and represents task that could be attached to Timer expiry for execution. Typical usage is:
TimerTask myTask = new TimerTask() { public void run() { ... the code to run ... } };
java.util.Timer
- this class is the actual timer that is scheduled for
expiry with its schedule
method:
timer.schedule(myTask, 1000*seconds);
Timer
can be scheduled for one-time execution by those methods:
public void schedule(TimerTask task, long delay)This schedules the timer to expire after delay milliseconds, or
public void schedule(TimerTask task, Date time)which schedules the timer to specified time in the future. If the time is in the past, the task is called immediately.
The Timer
can also be scheduled for repeated execution by:
public void schedule(TimerTask task, long delay, long period) public void schedule(TimerTask task, Date firstTime, long period)where delay (firstTime respectively) is the delay before the first execution (resp. the exact time of the first execution) and period is the time (in period ) before the next (and additional) executions. Scheduling using plain
schedule
can be very
inaccurate because n+1th call of the function is scheduled according to time of nth
call. This is called fixed-delay execution. It is inaccurate because of garbage collection in
VM etc.
The second method to schedule tasks is fixed-rate execution. In this method, tasks are scheduled according the real time approximately. n+1th call of the function is scheduled according to nth scheduled call. If something slows down, several close runs of the task can happen in the period less than period. This can't happen in fixed-delay execution.
The Timer
can be canceled by calling cancel
method:
public void cancel()The following is a small example of fixed-delay execution:
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.Timer; import java.util.TimerTask; public class TimerTaskDemo extends MIDlet implements CommandListener { private Command exitCommand; private Timer timer = new Timer(); private TextBox myScreen; private int seconds = 10; public void startApp () { Displayable current = Display.getDisplay(this).getCurrent(); if(current == null) { myScreen = new TextBox("TimerTask demo!", "You have 10 seconds to Exit...", 256, 0); exitCommand = new Command("Exit", Command.EXIT, 1); myScreen.addCommand(exitCommand); myScreen.setCommandListener(this); Display.getDisplay(this).setCurrent(myScreen); TimerTask counterTask = new TimerTask() { public void run() { myScreen.setString("Seconds left: " + Integer.toString(--seconds)); } }; TimerTask dismissTask = new TimerTask() { public void run() { notifyDestroyed(); } }; timer.schedule(counterTask, 1000, 1000); timer.schedule(dismissTask, seconds*1000); } } public void pauseApp() {} public void destroyApp(boolean b) {} public void commandAction(Command c, Displayable d) { notifyDestroyed(); } } |
counterTask
to execute in a second and execute repeatedly after
1 second. It also defines second task dismisstask
which is scheduled to destroy the
MIDlet in 10 seconds. The counterTask
changes the text of the TextBox
to
count down to 0:
CLDC specification defines only the generic framework for connections, but no actual protocols. This
is the role of profiles. MIDP profile v1.0 defines subset of the HTTP 1.1 protocol
(interface HttpConnection
) with GET, POST and HEAD methods.
javax.microedition.io.HttpConnection
interface is used for HTTP connection. The
following example shows the sample usage of it.
import javax.microedition.lcdui.*; import javax.microedition.midlet.*; import java.io.*; import javax.microedition.io.*; public class HttpConnectionExample extends MIDlet { private Display display; String url = "http://www.janik.cz/"; public HttpConnectionExample() { display = Display.getDisplay(this); } public void startApp() { try { getViaStreamConnection(url); } catch (IOException e) { } } public void pauseApp() {} public void destroyApp(boolean b) {} void getViaStreamConnection(String url) throws IOException { HttpConnection c = null; InputStream s = null; StringBuffer b = new StringBuffer(); TextBox t = null; try { c = (HttpConnection)Connector.open(url); c.setRequestProperty("User-Agent", "UnknownAgentUsingNokia7650"); s = c.openInputStream(); int ch; while((ch = s.read()) != -1) { b.append((char) ch); } t = new TextBox("Web page", b.toString(), 1024, 0); } finally { if(s != null) { s.close(); } if(c != null) { c.close(); } } display.setCurrent(t); } } |
HttpConnection
interface and using
its setRequestProperty
property. We set the User-Agent HTTP header to the string
"UnknownAgentUsingNokia7650" and it is shown as such to the server itself:
160.218.160.213 - - [...time...] \ "GET /www.janik.cz/ HTTP/1.1" 200 689 "" "UnknownAgentUsingNokia7650"
DataInputStream
, DataOutputStream
and ByteArrayInputStream
, ByteArrayOutputStream
. You can even store
images in records.
Every record is identified by record IDs. Record IDs are unique in one record store (see below). First record's ID is 1, second's 2, ... If you delete record with ID 2, and add new one, record ID 2 is NOT reused.
What is it record store? It is a collection of records (with their IDs running up from 1). Each record belongs to exactly one record store and are accessed through their record store. Record store is named. The name is case sensitive and may contains up to 32 Unicode characters. Only the MIDlet who created the record store and other MIDlets from the same MIDlet suite can access record store. This means that each and every MIDlet suite in the device can create record store with the same name and they will be distinct. MIDP version 1.0 does not allow sharing record stores between MIDlet suites, but version 2.0 allows that. When the MIDlet suite is removed/uninstalled from the device, all record stores created by it must be removed too.
There is no locking in the record store API, but the implementation must ensure that all operations
are atomic, synchronous and serialized. After each change, the record store is marked with timestamp
(in the form of System.currentTimeMillis()
) and version. If you want to track changes
in the record store, you can also register listener and be notified after each change.
Actual implementation of record management system is
inside javax.microedition.rms
. MIDP devices provide limited data storage, but the
specification requires at least 8k of non-volatile memory for persistent data. Applications can
specify the minimal required persistent data storage in their JAD file in
the MIDlet-Data-Size
property. Devices may refuse to install the application if there
is not enough space.
Really nice introductory article about RMS were written by Eric Giguere at Sun's website. Similar article was written by Qusay Mahmoud.
Now, it is the good time to explore the life cycle of a MIDlet. MIDlets are executes in a virtual entity referred to as Application Management Software (AMS). As our picture describes, MIDlet can be in several phases (when already installed on the device):
Display
. When the application receives focus from the AMS (at the start or when
returning from Paused state), startApp()
is called. Application then should call
appropriate startup page by Display.getDisplay(this).setCurrent()
function (when
starting up) or restore the page displayed before going to Paused state. When the application loose
the focus, pauseApp()
is called. No screen is displayed then, but all timers and tasks
are still running. The application can setCurrent()
the screen that should be presented
when returning from Paused state. The function destroyApp(boolean unconditionally)
is
called when application is being destroyed. No access to Display
, but the application
can/should save its state to persistent storage. The application can request to not enter the
Destroyed state by throwing MIDletStateChangeException
but only if
the unconditional
flag is set to false
.
MIDP specification contains the chapter about User Interface, using so called MIDP UI API. There are two APIs for two distinct ways of programming applications:
Displayable
. It is abstract class
extending Object
. Displayable
can be placed on the Display
thus shown to the user. It may have a title string, a ticker and zero or more commands and listener
attached to it. The actual visual presentation is defined by its subclasses.
MIDP 1.0 defines two subclasses of Displayable
:
Screen
- is the parent of all high-level UI
widgets: Alert
, TextBox
, List
and
generic Form
.Canvas
is the direct screen representation used in low-level API.Screen
in fact) that allows the user to enter and
edit text in the widget. This UI widget has the following constructor:
TextBox(String title, String text, int maxSize, int constraints)where
title
is the title of the TextBox
, text
is its initial
contents, maxSize
identify the maximal number of character programmer wants it to
contains (see below) and constraints
allows the programmer to modify its behavior.
The maximal size of the text inside TextBox
is device dependent so application should
check getMaxSize()
for valid sizes. It is not the size of the displayed text
though. The displayed text may be shorter and it is device's responsibility to allow scrolling in
it.
Constraints allow programmer to specify which value is to be entered. They are constants defined
in the class TextField
:
ANY
EMAILADDR
NUMERIC
PHONENUMBER
URL
DECIMAL
TextBox
code for the TextBox
on this
image. The title is displayed twice on the screen, but the real device displays title only once (at
the top of the screen). Ticker is running from the right to the left side of the screen just below
the title. The application can't set its direction nor speed, it is decided by the
implementation. If there are linebreaks in the ticker text, emulator removes them, but the real
device uses empty boxes - so do not use them. Several screens can reuse the same ticker
(see setTicker()
method of Screen
). The
modifier TextField.ANY
allows any text to be typed. If you change it
to TextField.PASSWORD
, only starts will be displayed instead of the actual text.
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class UITextBox extends MIDlet implements CommandListener { private Command exitCommand, showCommand; private TextBox uiTextBox; public UITextBox() {} public void startApp() { Displayable current = Display.getDisplay(this).getCurrent(); if(current == null) { uiTextBox = new TextBox("UITextBox title", "The initial contents of the uiTextBox", 256, TextField.ANY); exitCommand = new Command("Exit", Command.EXIT, 1); showCommand = new Command("Show the text", Command.SCREEN, 1); uiTextBox.addCommand(exitCommand); uiTextBox.addCommand(showCommand); uiTextBox.setCommandListener(this); Ticker uiTicker = new Ticker ("This is a ticker!"); uiTextBox.setTicker(uiTicker); Display.getDisplay(this).setCurrent(uiTextBox); } } public void pauseApp() {} public void destroyApp(boolean b) {} public void commandAction(Command c, Displayable d) { if (c == exitCommand) { destroyApp(false); notifyDestroyed(); } else if (c == showCommand) { // This is only useful for the emulator. System.out.println(uiTextBox.getString()); } } } |
-----