/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.midlet;
import javax.microedition.io.ConnectionNotFoundException;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import com.sun.midp.security.Permissions;
import com.sun.midp.security.SecurityToken;
/**
* MIDletPeer maintains the current state of the MIDlet and forwards updates
* to it. It contains references to the MIDlet itself and to its
* corresponding Display instance. Control methods (startApp, destroyApp,
* pauseApp) defined here are invoked on the MIDlet object via the
* MIDletTunnel.
* <p>
* All state changes are synchronized using midletStateHandler retrieved
* from the MIDletStateHandler.
* NotifyPaused, ResumeRequest, and NotifyDestroyed methods invoked on the
* MIDlet cause the appropriate state change. The MIDletStateHandler is aware
* of changes by waiting on the midletStateHandler.
*/
public class MIDletPeer implements MIDletEventConsumer {
/*
* Implementation state; the states are in priority order.
* That is, a higher number indicates a preference to be
* selected for activating sooner. This allows the MIDlet state handler
* to make one pass over the known MIDlets and pick the
* "best" MIDlet to activate.
*/
/**
* State of the MIDlet is Paused; it should be quiescent
*/
public static final int PAUSED = 0;
/**
* State of the MIDlet is Active
*/
public static final int ACTIVE = 1;
/**
* State of the MIDlet when resumed by the display manager
*/
static final int ACTIVE_PENDING = 2;
/**
* State of the MIDlet when paused by the display manager
*/
static final int PAUSE_PENDING = 3;
/**
* State of the MIDlet with destroy pending
*/
static final int DESTROY_PENDING = 4;
/**
* State of the MIDlet is Destroyed
*/
public static final int DESTROYED = 5;
/** The controller of MIDlets. */
private static MIDletStateHandler midletStateHandler;
/** The call when a MIDlet's state changes. */
private static MIDletStateListener midletStateListener;
/** Handles platform requests. */
private static PlatformRequest platformRequest;
/** The MIDletTunnel implementation from javax.microedition.midlet */
private static MIDletTunnel tunnel;
/**
* Initialize the MIDletPeer class. Should only be called by the
* MIDletPeerList (MIDletStateHandler).
*
* @param theMIDletStateHandler the midlet state handler
* @param theMIDletStateListener the midlet state listener
* @param thePlatformRequestHandler the platform request handler
*/
static void initClass(
MIDletStateHandler theMIDletStateHandler,
MIDletStateListener theMIDletStateListener,
PlatformRequest thePlatformRequestHandler) {
midletStateHandler = theMIDletStateHandler;
midletStateListener = theMIDletStateListener;
platformRequest = thePlatformRequestHandler;
}
/**
* Sets up the reference to the MIDletTunnel implementation.
* This must be called exactly once during system initialization.
*
* @param token security token for authorizing the caller
* @param t the MIDletTunnel implementation
*/
public static void setMIDletTunnel(SecurityToken token, MIDletTunnel t) {
token.checkIfPermissionAllowed(Permissions.MIDP);
tunnel = t;
}
/**
* Returns the MIDletPeer object corresponding to the given
* midlet instance.
*
* @param m the midlet instance
*
* @return MIDletPeer instance associate with m
*/
static MIDletPeer getMIDletPeer(MIDlet m) {
return tunnel.getMIDletPeer(m);
}
/**
* The applications current state.
*/
private int state;
/**
* The MIDlet for which this is the state.
*/
protected MIDlet midlet;
/**
* Creates a MIDlet's peer which is registered the MIDletStateHandler.
* Shall be called only from MIDletStateHandler.
* <p>
* The peer MIDlet field is set later when the MIDlet's constructor calls
* newMidletState.
*/
MIDletPeer() {
state = ACTIVE_PENDING; // So it will be made active soon
}
/**
* Get the MIDlet for which this holds the state.
*
* @return the MIDlet; will not be null.
*/
public MIDlet getMIDlet() {
return midlet;
}
/**
* Forwards startApp to the MIDlet.
*
* @exception <code>MIDletStateChangeException</code> is thrown if the
* <code>MIDlet</code> cannot start now but might be able
* to start at a later time.
*/
void startApp() throws MIDletStateChangeException {
tunnel.callStartApp(midlet);
}
/**
* Forwards pauseApp to the MIDlet.
*
*/
void pauseApp() {
tunnel.callPauseApp(midlet);
}
/**
* Forwards destoryApp to the MIDlet.
*
* @param unconditional the flag to pass to destroy
*
* @exception <code>MIDletStateChangeException</code> is thrown
* if the <code>MIDlet</code>
* wishes to continue to execute (Not enter the <i>Destroyed</i>
* state).
* This exception is ignored if <code>unconditional</code>
* is equal to <code>true</code>.
*/
void destroyApp(boolean unconditional)
throws MIDletStateChangeException {
tunnel.callDestroyApp(midlet, unconditional);
}
/**
*
* Used by a <code>MIDlet</code> to notify the application management
* software that it has entered into the
* <i>DESTROYED</i> state. The application management software will not
* call the MIDlet's <code>destroyApp</code> method, and all resources
* held by the <code>MIDlet</code> will be considered eligible for
* reclamation.
* The <code>MIDlet</code> must have performed the same operations
* (clean up, releasing of resources etc.) it would have if the
* <code>MIDlet.destroyApp()</code> had been called.
*
*/
public final void notifyDestroyed() {
synchronized (midletStateHandler) {
state = DESTROYED;
midletStateHandler.notify();
}
}
/**
* Used by a <code>MIDlet</code> to notify the application management
* software that it has entered into the <i>PAUSED</i> state.
* Invoking this method will
* have no effect if the <code>MIDlet</code> is destroyed,
* or if it has not yet been started. <p>
* It may be invoked by the <code>MIDlet</code> when it is in the
* <i>ACTIVE</i> state. <p>
*
* If a <code>MIDlet</code> calls <code>notifyPaused()</code>, in the
* future its <code>startApp()</code> method may be called make
* it active again, or its <code>destroyApp()</code> method may be
* called to request it to destroy itself.
*/
public final void notifyPaused() {
int oldState;
synchronized (midletStateHandler) {
oldState = state;
/*
* do not notify the midletStateHandler,
* since there is nothing to do
*/
setStateWithoutNotify(PAUSED);
}
// do work after releasing the lock
if (oldState == ACTIVE) {
midletStateListener.midletPausedItself(getMIDletSuite(),
getMIDlet().getClass().getName());
}
}
/**
* Provides a <code>MIDlet</code> with a mechanism to retrieve
* <code>MIDletSuite</code> for this MIDlet.
*
* @return MIDletSuite for this MIDlet
*/
public final MIDletSuite getMIDletSuite() {
return midletStateHandler.getMIDletSuite();
}
/**
* Used by a <code>MIDlet</code> to notify the application management
* software that it is
* interested in entering the <i>ACTIVE</i> state. Calls to
* this method can be used by the application management software to
* determine which applications to move to the <i>ACTIVE</i> state.
* <p>
* When the application management software decides to activate this
* application it will call the <code>startApp</code> method.
* <p> The application is generally in the <i>PAUSED</i> state when this is
* called. Even in the paused state the application may handle
* asynchronous events such as timers or callbacks.
*/
public final void resumeRequest() {
midletStateListener.resumeRequest(getMIDletSuite(),
getMIDlet().getClass().getName());
}
/**
* Requests that the device handle (e.g. display or install)
* the indicated URL.
*
* <p>If the platform has the appropriate capabilities and
* resources available, it SHOULD bring the appropriate
* application to the foreground and let the user interact with
* the content, while keeping the MIDlet suite running in the
* background. If the platform does not have appropriate
* capabilities or resources available, it MAY wait to handle the
* URL request until after the MIDlet suite exits. In this case,
* when the requesting MIDlet suite exits, the platform MUST then
* bring the appropriate application to the foreground to let the
* user interact with the content.</p>
*
* <p>This is a non-blocking method. In addition, this method does
* NOT queue multiple requests. On platforms where the MIDlet
* suite must exit before the request is handled, the platform
* MUST handle only the last request made. On platforms where the
* MIDlet suite and the request can be handled concurrently, each
* request that the MIDlet suite makes MUST be passed to the
* platform software for handling in a timely fashion.</p>
*
* <p>If the URL specified refers to a MIDlet suite (either an
* Application Descriptor or a JAR file), the request is
* interpreted as a request to install the named package. In this
* case, the platform's normal MIDlet suite installation process
* SHOULD be used, and the user MUST be allowed to control the
* process (including cancelling the download and/or
* installation). If the MIDlet suite being installed is an
* <em>update</em> of the currently running MIDlet suite, the
* platform MUST first stop the currently running MIDlet suite
* before performing the update. On some platforms, the currently
* running MIDlet suite MAY need to be stopped before any
* installations can occur.</p>
*
* <p>If the URL specified is of the form
* <code>tel:<number></code>, as specified in <a
* href="http://rfc.net/rfc2806.html">RFC2806</a>, then the
* platform MUST interpret this as a request to initiate a voice
* call. The request MUST be passed to the "phone"
* application to handle if one is present in the platform.</p>
*
* <p>Devices MAY choose to support additional URL schemes beyond
* the requirements listed above.</p>
*
* <p>Many of the ways this method will be used could have a
* financial impact to the user (e.g. transferring data through a
* wireless network, or initiating a voice call). Therefore the
* platform MUST ask the user to explicitly acknowledge each
* request before the action is taken. Implementation freedoms are
* possible so that a pleasant user experience is retained. For
* example, some platforms may put up a dialog for each request
* asking the user for permission, while other platforms may
* launch the appropriate application and populate the URL or
* phone number fields, but not take the action until the user
* explicitly clicks the load or dial buttons.</p>
*
* @return true if the MIDlet suite MUST first exit before the
* content can be fetched.
*
* @param URL The URL for the platform to load.
*
* @exception ConnectionNotFoundException if
* the platform cannot handle the URL requested.
*
*/
public final boolean platformRequest(String URL)
throws ConnectionNotFoundException {
return platformRequest.dispatch(URL);
}
/**
* Change the state and notify.
* Check to make sure the new state makes sense.
* Changes to the status are protected by the midletStateHandler.
* Any change to the state notifies the midletStateHandler.
*
* @param newState new state of the MIDlet
*/
void setState(int newState) {
synchronized (midletStateHandler) {
setStateWithoutNotify(newState);
midletStateHandler.notify();
}
}
/**
* Get the status of the specified permission.
* If no API on the device defines the specific permission
* requested then it must be reported as denied.
* If the status of the permission is not known because it might
* require a user interaction then it should be reported as unknown.
*
* @param permission to check if denied, allowed, or unknown.
* @return 0 if the permission is denied; 1 if the permission is allowed;
* -1 if the status is unknown
*/
public int checkPermission(String permission) {
return getMIDletSuite().checkPermission(permission);
}
/**
* Change the state without notifying the MIDletStateHandler.
* Check to make sure the new state makes sense.
* <p>
* To be called only by the MIDletStateHandler or MIDletState while holding
* the lock on midletStateHandler.
*
* @param newState new state of the MIDlet
*/
void setStateWithoutNotify(int newState) {
switch (state) {
case DESTROYED:
// can't set any thing else
return;
case DESTROY_PENDING:
if (newState != DESTROYED) {
// can only set DESTROYED
return;
}
break;
case PAUSED:
if (newState == PAUSE_PENDING) {
// already paused by app
return;
}
break;
case PAUSE_PENDING:
if (newState == ACTIVE_PENDING) {
/*
* pausedApp has not been called so the state
* can be set to active to cancel the pending pauseApp.
*/
state = ACTIVE;
return;
}
break;
case ACTIVE:
if (newState == ACTIVE_PENDING) {
// already active
return;
}
break;
case ACTIVE_PENDING:
if (newState == PAUSE_PENDING) {
/*
* startApp has not been called so the state
* can be set to paused to cancel the pending startApp.
*/
state = PAUSED;
return;
}
break;
}
state = newState;
}
/**
* Get the state.
*
* @return current state of the MIDlet.
*/
int getState() {
synchronized (midletStateHandler) {
return state;
}
}
/**
* Pause a MIDlet.
* MIDletEventConsumer I/F method.
*/
public void handleMIDletPauseEvent() {
setState(MIDletPeer.PAUSE_PENDING);
}
/**
* Activate a MIDlet.
* MIDletEventConsumer I/F method.
*/
public void handleMIDletActivateEvent() {
setState(MIDletPeer.ACTIVE_PENDING);
}
/**
* Destroy a MIDlet.
* MIDletEventConsumer I/F method.
*/
public void handleMIDletDestroyEvent() {
setState(MIDletPeer.DESTROY_PENDING);
}
}
|