MIDletStateHandlerpublic class MIDletStateHandler extends Object The MIDletStateHandler starts and controls MIDlets through the lifecycle
states.
MIDlets are created using its no-arg Constructor. Once created
a MIDlet is sequenced through the ACTIVE ,
PAUSED , and DESTROYED states.
The MIDletStateHandler is a singleton for the suite being run and
is retrieved with getMIDletStateHandler(). This allow the
MIDletStateHandler to be the anchor of trust internally for the MIDP API,
restricted methods can obtain the MIDletStateHandler for a MIDlet suite
inorder to check the properties and actions of a suite.
Because of this, there MUST only be one a MIDlet suite per
MIDletStateHandler. In addition a method can assume that the application
manager is the caller if there is no suite started.
The MIDlet methods are protected in the javax.microedition.midlet package
so the MIDletStateHandler can not call them directly. The MIDletState
object and
MIDletTunnel subclass class allow the MIDletStateHandler to hold the state
of the
MIDlet and to invoke methods on it. The MIDletState instance is created
by the MIDlet when it is constructed.
This implementation of the MIDletStateHandler introduces
extra internal MIDlet states to allow processing of MIDlet
requested state changes in the control thread and serialized with
other state changes. The additional states are:
-
ACTIVE_PENDING - The MIDlet is still PAUSED but
will be ACTIVE after startApp is called when the state is
next processed.
-
PAUSE_PENDING - The MIDlet is still ACTIVE but
will be PAUSED after pauseApp is called when the state is
next processed.
-
DESTROY_PENDING - Indicates that the MIDlet needs
to be DESTROYED . The MIDlet's destroyApp has not yet been
called.
The MIDletStateHandler loops, looking for MIDlets that require state changes
and making the requested changes including calling methods in
the MIDlet to make the change.
When a MIDlet's state is changed to ACTIVE ,
PAUSED , or DESTROYED the MIDlet state listener
is notified of the change, which in turn sends the notification onto
the central AMS. |
Fields Summary |
---|
private MIDletSuite | midletSuitethe current MIDlet suite. | private MIDletLoader | midletLoaderloads the MIDlets from a suite's JAR in a VM specific way. | private MIDletPeer[] | midletsarray of MIDlets. | private int | nmidletscurrent number of MIDlets [0..n-1]. | private int | scanIndexnext index to be scanned by selectForeground. | private static MIDletStateHandler | stateHandlerThe event handler of all MIDlets in an Isolate. | private static MIDletStateListener | listenerThe listener for the state of all MIDlets in an Isolate. | private static Object | createMIDletLockSerializes the creation of MIDlets. | private static MIDletPeer | newMidletPeerNew MIDlet peer waiting for the next MIDlet created to claim it. | private MIDletPeer | underConstructionPeerMIDlet peer for MIDlet being constructed but not registered yet. |
Constructors Summary |
---|
private MIDletStateHandler()Construct a new MIDletStateHandler object.
nmidlets = 0;
// start with 5 empty slots, we will add more if needed
midlets = new MIDletPeer[5];
|
Methods Summary |
---|
private void | createAndRegisterMIDlet(int externalAppId, java.lang.String classname)Creates and register MIDlet with VM notification
of the MIDlet's startup phase.
listener.midletPreStart(getMIDletSuite(), classname);
register(createMIDlet(externalAppId, classname));
| private javax.microedition.midlet.MIDlet | createMIDlet(int externalAppId, java.lang.String classname)Creates a MIDlet.
MIDlet midlet = null;
synchronized (createMIDletLock) {
/*
* Just in case there is a hole we have not found.
* Make sure there is not a new MIDlet state already created.
*/
if (newMidletPeer != null) {
throw new SecurityException("Recusive MIDlet creation");
}
newMidletPeer = new MIDletPeer();
underConstructionPeer = newMidletPeer;
try {
/*
* We can send a MIDlet create event because the peer that
* the AMS uses has been created.
*/
listener.midletCreated(getMIDletSuite(), classname,
externalAppId);
try {
midlet = midletLoader.newInstance(getMIDletSuite(),
classname);
return midlet;
} finally {
if (midlet == null) {
/*
* The MIDlet was not constructed, send destroy
* notification to remove the peer from any lists.
*/
listener.midletDestroyed(getMIDletSuite(), classname);
}
}
} finally {
/* Make sure the creation window is closed. */
newMidletPeer = null;
}
}
| public void | destroySuite()Destroys all running MIDlets in this suite only. This method is only
used by the push registry in single VM mode.
synchronized (this) {
for (int i = 0; i < nmidlets; i++) {
if (midlets[i].getState() != MIDletPeer.DESTROYED) {
midlets[i].
setStateWithoutNotify(MIDletPeer.DESTROY_PENDING);
}
}
this.notify();
}
| private int | findMIDletByClass(MIDletPeer m)Finds a MIDlet in the list by it class.
Only a single MIDlet of a class can be active at
a time.
Must be called synchronized on "this".
// Find it in the list
for (int i = 0; i < nmidlets; i++) {
if (m.getMIDlet().getClass() ==
midlets[i].getMIDlet().getClass()) {
return i;
}
}
return -1;
| public java.lang.String | getFirstRunningMidlet()Gets the class name first midlet in the list of running MIDlets.
synchronized (this) {
if (nmidlets <= 0) {
return null;
}
return midlets[0].midlet.getClass().getName();
}
| public MIDletEventConsumer | getMIDletEventConsumer(com.sun.midp.security.SecurityToken token, java.lang.String name)Gets MIDlet event consumer of the named MIDlet .
token.checkIfPermissionAllowed(Permissions.AMS);
synchronized (this) {
for (int i = 0; i < nmidlets; i++) {
if (midlets[i].getMIDlet().getClass().getName().equals(name)) {
return midlets[i];
}
}
}
return null;
| public static int | getMIDletState(javax.microedition.midlet.MIDlet midlet)Retrieves current state of the MIDlet given.
return MIDletPeer.getMIDletPeer(midlet).getState();
| public MIDletSuite | getMIDletSuite()Provides a object with a mechanism to retrieve
MIDletSuite being run.
return midletSuite;
| public static synchronized com.sun.midp.midlet.MIDletStateHandler | getMidletStateHandler()Gets the MIDletStateHandler that manages the lifecycle states of
MIDlets running in an Isolate.
If the instance of the MIDletStateHandler has already been created
it is returned. If not it is created.
The instance becomes the MIDletStateHandler for this suite.
The fact that there is one handler per Isolate
is a security feature. Also a security feature, is that
getMIDletStateHandler is
static, so API can find out what suite is calling, if in the future
multiple suites can be run in the same VM, the MIDlet state handler
for each suite
should be loaded in a different classloader or have some other way
having multiple instances of static class data.
/*
* If the midlet state handler has not been created, create one now.
*/
if (stateHandler == null) {
/* This is the default scheduler class */
stateHandler = new MIDletStateHandler();
}
return stateHandler;
| public void | initMIDletStateHandler(com.sun.midp.security.SecurityToken token, MIDletStateListener theMIDletStateListener, MIDletLoader theMidletLoader, PlatformRequest thePlatformRequestHandler)Initializes MIDlet State Handler.
token.checkIfPermissionAllowed(Permissions.AMS);
listener = theMIDletStateListener;
midletLoader = theMidletLoader;
MIDletPeer.initClass(this, listener, thePlatformRequestHandler);
| public boolean | isRunning(java.lang.String name)Checks if the named MIDlet has already been instantiated.
boolean found = false;
synchronized (this) {
if (underConstructionPeer != null &&
underConstructionPeer.getMIDlet().
getClass().getName().equals(name)) {
found = true;
} else {
for (int i = 0; i < nmidlets; i++) {
if (midlets[i].getMIDlet().
getClass().getName().equals(name)) {
// found only if has not been destroyed
found = (midlets[i].getState() != MIDletPeer.DESTROYED);
break;
}
}
}
}
return found;
| public static MIDletPeer | newMIDletPeer(com.sun.midp.security.SecurityToken token, javax.microedition.midlet.MIDlet m)Called by the MIDlet constructor to a new MIDletPeer object.
token.checkIfPermissionAllowed(Permissions.MIDP);
synchronized (createMIDletLock) {
MIDletPeer temp;
if (newMidletPeer == null) {
throw new SecurityException(
"MIDlet not constructed by createMIDlet.");
}
temp = newMidletPeer;
newMidletPeer = null;
temp.midlet = m;
return temp;
}
| private void | register(javax.microedition.midlet.MIDlet midlet)Registers a MIDlet being constructed.
synchronized (this) {
MIDletPeer state = MIDletPeer.getMIDletPeer(midlet);
/*
* If a MIDlet of the same class is already running
* Make the existing MIDlet current so that startSuite()
* will run it
*/
int i = findMIDletByClass(state);
if (i >= 0) {
state.setState(MIDletPeer.DESTROY_PENDING);
// Fall into adding it to the list so destroyApp
// can be called at a reasonable time.
}
// Grow the list if necessary
if (nmidlets >= midlets.length) {
MIDletPeer[] n = new MIDletPeer[nmidlets+5];
System.arraycopy(midlets, 0, n, 0, nmidlets);
midlets = n;
}
// Add it to the end of the list
midlets[nmidlets++] = state;
// MIDlet peer is registered now
underConstructionPeer = null;
this.notify();
}
| private MIDletPeer | selectByPriority()Looks through the current MIDlets and select one to
be processed.
Note: that this method is called while synchronized on "this"
MIDletPeer found = null; // Chosen MIDletPeer
int state = -1; // the state of the chosen MIDlet
/*
* Find the most desirable MIDlet based on its state
* The higher state values are preferred because they
* are needed for cleanup.
*/
for (int i = nmidlets-1; i >= 0; i--) {
// make sure index is inside current array, favoring the end
if (scanIndex < 0 || scanIndex >= nmidlets)
scanIndex = nmidlets-1;
// Pick this MIDlet if the state is higher priority
int s = midlets[scanIndex].getState();
if (s > state) {
found = midlets[scanIndex];
state = s;
}
scanIndex--;
}
return found;
| public void | startMIDlet(java.lang.String classname, java.lang.String displayName)Starts a MIDlet from outside of the package.
startMIDlet(0, classname, displayName);
| public void | startMIDlet(int externalAppId, java.lang.String classname, java.lang.String displayName)Starts a MIDlet from outside of the package.
midletSuite.checkIfPermissionAllowed(Permissions.AMS);
createAndRegisterMIDlet(externalAppId, classname);
| public void | startMIDlet(com.sun.midp.security.SecurityToken token, java.lang.String classname, java.lang.String displayName)Starts a MIDlet from outside of the package.
startMIDlet(token, 0, classname, displayName);
| public void | startMIDlet(com.sun.midp.security.SecurityToken token, int externalAppId, java.lang.String classname, java.lang.String displayName)Starts a MIDlet from outside of the package.
token.checkIfPermissionAllowed(Permissions.AMS);
createAndRegisterMIDlet(externalAppId, classname);
| public void | startSuite(MIDletSuiteExceptionListener exceptionHandler, MIDletSuite aMidletSuite, int externalAppId, java.lang.String classname)Runs MIDlets until there are none.
Handle any pending state transitions of any MIDlet.
If there are none, wait for transitions.
If there is no foreground MIDlet select one that is ACTIVE and
has setCurrent != null.
If the foreground MIDlet changes from the ACTIVE_FOREGROUND state
it automatically looses the foreground and and new one is selected.
if (midletSuite != null) {
throw new RuntimeException(
"There is already a MIDlet Suite running.");
}
midletSuite = aMidletSuite;
createAndRegisterMIDlet(externalAppId, classname);
/*
* Until there are no MIDlets
* Scan all the MIDlets looking for state changes.
*/
while (nmidlets > 0) {
try {
MIDletPeer curr;
int state;
/*
* A MIDlet can change the MIDlet concurrently.
* the MIDlet state handler this is used to
* synchronize these changes. However any calls to outside of
* this package should NOT be done holding
* "this".
* For this reason there are 2 phases each with a switch
* statement to process a state.
*
* The state is obtained and changed before the work is
* done so that when "this" is released to
* perform external calls for that state, any state change done
* by the MIDlet concurrently are not lost.
*/
synchronized (this) {
/*
* Find the highest priority state of any MIDlet and
* process, but do not hold the lock while processing
* to avoid deadlocks with LCDUI and event handling.
* Perform state changes with a lock so
* no state changes are lost.
*/
curr = selectByPriority();
state = curr.getState();
switch (state) {
case MIDletPeer.ACTIVE:
// fall through
case MIDletPeer.PAUSED:
// Wait for some change in the state of a MIDlet
// that needs attention
try {
this.wait();
} catch (InterruptedException e) {
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING,
LogChannels.LC_AMS,
"InterruptedException " +
"during mutex wait");
}
}
continue;
case MIDletPeer.ACTIVE_PENDING:
// Start the MIDlet
curr.setStateWithoutNotify(MIDletPeer.ACTIVE);
break;
case MIDletPeer.PAUSE_PENDING:
// The system wants the MIDlet paused
curr.setStateWithoutNotify(MIDletPeer.PAUSED);
break;
case MIDletPeer.DESTROY_PENDING:
curr.setStateWithoutNotify(MIDletPeer.DESTROYED);
break;
case MIDletPeer.DESTROYED:
unregister(curr);
break;
default:
throw new Error("Illegal MIDletPeer state " +
curr.getState());
}
}
/** perform work that may block outside of "this" */
switch (state) {
case MIDletPeer.ACTIVE_PENDING:
try {
listener.preActivated(getMIDletSuite(),
curr.getMIDlet().getClass().getName());
curr.startApp();
} catch (Throwable ex) {
if (Logging.TRACE_ENABLED) {
Logging.trace(ex, "startApp threw an Exception");
}
curr.setState(MIDletPeer.DESTROY_PENDING);
exceptionHandler.handleException(ex);
break;
}
/*
* The actual state of the MIDlet is already active.
* But any notifications done after startApp call.
*/
listener.midletActivated(getMIDletSuite(),
curr.getMIDlet());
break;
case MIDletPeer.PAUSE_PENDING:
try {
curr.pauseApp();
} catch (Throwable ex) {
if (Logging.TRACE_ENABLED) {
Logging.trace(ex, "pauseApp threw an Exception");
}
curr.setState(MIDletPeer.DESTROY_PENDING);
exceptionHandler.handleException(ex);
break;
}
/*
* The actual state of the MIDlet is already paused.
* But any notifications done after pauseApp() call.
*/
listener.midletPaused(getMIDletSuite(),
curr.getMIDlet().getClass().getName());
break;
case MIDletPeer.DESTROY_PENDING:
// If the MIDlet is in the DESTROY_PENDING state
// call its destroyApp method to clean it up.
try {
// Tell the MIDlet to cleanup.
curr.destroyApp(true);
} catch (MIDletStateChangeException ex) {
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING,
LogChannels.LC_AMS,
"destroyApp threw a " +
"MIDletStateChangeException");
}
exceptionHandler.handleException(ex);
} catch (Throwable ex) {
if (Logging.TRACE_ENABLED) {
Logging.trace(ex, "destroyApp threw an Exception");
}
exceptionHandler.handleException(ex);
}
break;
case MIDletPeer.DESTROYED:
listener.midletDestroyed(getMIDletSuite(),
curr.getMIDlet().getClass().getName());
break;
}
} catch (Throwable ex) {
if (Logging.TRACE_ENABLED) {
Logging.trace(ex, "Exception in startSuite");
}
exceptionHandler.handleException(ex);
}
}
| private void | unregister(MIDletPeer m)Removes a MIDlet from the list if it is there,
otherwise ignore the request.
Call only while synchronized on "this".
// Find it in the list and switch the last one for it.
for (int i = 0; i < nmidlets; i++) {
if (m == midlets[i]) {
// Switch the last MIDlet into that offset.
midlets[i] = midlets[nmidlets-1];
// null out from array and remove from map to allow for GC
midlets[--nmidlets] = null;
break;
}
}
|
|