001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.midlet;
028:
029: import javax.microedition.io.ConnectionNotFoundException;
030:
031: import javax.microedition.midlet.MIDlet;
032: import javax.microedition.midlet.MIDletStateChangeException;
033:
034: import com.sun.midp.security.Permissions;
035: import com.sun.midp.security.SecurityToken;
036:
037: /**
038: * MIDletPeer maintains the current state of the MIDlet and forwards updates
039: * to it. It contains references to the MIDlet itself and to its
040: * corresponding Display instance. Control methods (startApp, destroyApp,
041: * pauseApp) defined here are invoked on the MIDlet object via the
042: * MIDletTunnel.
043: * <p>
044: * All state changes are synchronized using midletStateHandler retrieved
045: * from the MIDletStateHandler.
046: * NotifyPaused, ResumeRequest, and NotifyDestroyed methods invoked on the
047: * MIDlet cause the appropriate state change. The MIDletStateHandler is aware
048: * of changes by waiting on the midletStateHandler.
049: */
050:
051: public class MIDletPeer implements MIDletEventConsumer {
052: /*
053: * Implementation state; the states are in priority order.
054: * That is, a higher number indicates a preference to be
055: * selected for activating sooner. This allows the MIDlet state handler
056: * to make one pass over the known MIDlets and pick the
057: * "best" MIDlet to activate.
058: */
059:
060: /**
061: * State of the MIDlet is Paused; it should be quiescent
062: */
063: public static final int PAUSED = 0;
064:
065: /**
066: * State of the MIDlet is Active
067: */
068: public static final int ACTIVE = 1;
069:
070: /**
071: * State of the MIDlet when resumed by the display manager
072: */
073: static final int ACTIVE_PENDING = 2;
074:
075: /**
076: * State of the MIDlet when paused by the display manager
077: */
078: static final int PAUSE_PENDING = 3;
079:
080: /**
081: * State of the MIDlet with destroy pending
082: */
083: static final int DESTROY_PENDING = 4;
084:
085: /**
086: * State of the MIDlet is Destroyed
087: */
088: public static final int DESTROYED = 5;
089:
090: /** The controller of MIDlets. */
091: private static MIDletStateHandler midletStateHandler;
092:
093: /** The call when a MIDlet's state changes. */
094: private static MIDletStateListener midletStateListener;
095:
096: /** Handles platform requests. */
097: private static PlatformRequest platformRequest;
098:
099: /** The MIDletTunnel implementation from javax.microedition.midlet */
100: private static MIDletTunnel tunnel;
101:
102: /**
103: * Initialize the MIDletPeer class. Should only be called by the
104: * MIDletPeerList (MIDletStateHandler).
105: *
106: * @param theMIDletStateHandler the midlet state handler
107: * @param theMIDletStateListener the midlet state listener
108: * @param thePlatformRequestHandler the platform request handler
109: */
110: static void initClass(MIDletStateHandler theMIDletStateHandler,
111: MIDletStateListener theMIDletStateListener,
112: PlatformRequest thePlatformRequestHandler) {
113:
114: midletStateHandler = theMIDletStateHandler;
115: midletStateListener = theMIDletStateListener;
116: platformRequest = thePlatformRequestHandler;
117: }
118:
119: /**
120: * Sets up the reference to the MIDletTunnel implementation.
121: * This must be called exactly once during system initialization.
122: *
123: * @param token security token for authorizing the caller
124: * @param t the MIDletTunnel implementation
125: */
126: public static void setMIDletTunnel(SecurityToken token,
127: MIDletTunnel t) {
128: token.checkIfPermissionAllowed(Permissions.MIDP);
129:
130: tunnel = t;
131: }
132:
133: /**
134: * Returns the MIDletPeer object corresponding to the given
135: * midlet instance.
136: *
137: * @param m the midlet instance
138: *
139: * @return MIDletPeer instance associate with m
140: */
141: static MIDletPeer getMIDletPeer(MIDlet m) {
142: return tunnel.getMIDletPeer(m);
143: }
144:
145: /**
146: * The applications current state.
147: */
148: private int state;
149:
150: /**
151: * The MIDlet for which this is the state.
152: */
153: protected MIDlet midlet;
154:
155: /**
156: * Creates a MIDlet's peer which is registered the MIDletStateHandler.
157: * Shall be called only from MIDletStateHandler.
158: * <p>
159: * The peer MIDlet field is set later when the MIDlet's constructor calls
160: * newMidletState.
161: */
162: MIDletPeer() {
163: state = ACTIVE_PENDING; // So it will be made active soon
164: }
165:
166: /**
167: * Get the MIDlet for which this holds the state.
168: *
169: * @return the MIDlet; will not be null.
170: */
171: public MIDlet getMIDlet() {
172: return midlet;
173: }
174:
175: /**
176: * Forwards startApp to the MIDlet.
177: *
178: * @exception <code>MIDletStateChangeException</code> is thrown if the
179: * <code>MIDlet</code> cannot start now but might be able
180: * to start at a later time.
181: */
182: void startApp() throws MIDletStateChangeException {
183: tunnel.callStartApp(midlet);
184: }
185:
186: /**
187: * Forwards pauseApp to the MIDlet.
188: *
189: */
190: void pauseApp() {
191: tunnel.callPauseApp(midlet);
192: }
193:
194: /**
195: * Forwards destoryApp to the MIDlet.
196: *
197: * @param unconditional the flag to pass to destroy
198: *
199: * @exception <code>MIDletStateChangeException</code> is thrown
200: * if the <code>MIDlet</code>
201: * wishes to continue to execute (Not enter the <i>Destroyed</i>
202: * state).
203: * This exception is ignored if <code>unconditional</code>
204: * is equal to <code>true</code>.
205: */
206: void destroyApp(boolean unconditional)
207: throws MIDletStateChangeException {
208: tunnel.callDestroyApp(midlet, unconditional);
209: }
210:
211: /**
212: *
213: * Used by a <code>MIDlet</code> to notify the application management
214: * software that it has entered into the
215: * <i>DESTROYED</i> state. The application management software will not
216: * call the MIDlet's <code>destroyApp</code> method, and all resources
217: * held by the <code>MIDlet</code> will be considered eligible for
218: * reclamation.
219: * The <code>MIDlet</code> must have performed the same operations
220: * (clean up, releasing of resources etc.) it would have if the
221: * <code>MIDlet.destroyApp()</code> had been called.
222: *
223: */
224: public final void notifyDestroyed() {
225: synchronized (midletStateHandler) {
226: state = DESTROYED;
227: midletStateHandler.notify();
228: }
229: }
230:
231: /**
232: * Used by a <code>MIDlet</code> to notify the application management
233: * software that it has entered into the <i>PAUSED</i> state.
234: * Invoking this method will
235: * have no effect if the <code>MIDlet</code> is destroyed,
236: * or if it has not yet been started. <p>
237: * It may be invoked by the <code>MIDlet</code> when it is in the
238: * <i>ACTIVE</i> state. <p>
239: *
240: * If a <code>MIDlet</code> calls <code>notifyPaused()</code>, in the
241: * future its <code>startApp()</code> method may be called make
242: * it active again, or its <code>destroyApp()</code> method may be
243: * called to request it to destroy itself.
244: */
245: public final void notifyPaused() {
246: int oldState;
247:
248: synchronized (midletStateHandler) {
249: oldState = state;
250:
251: /*
252: * do not notify the midletStateHandler,
253: * since there is nothing to do
254: */
255: setStateWithoutNotify(PAUSED);
256: }
257:
258: // do work after releasing the lock
259: if (oldState == ACTIVE) {
260: midletStateListener.midletPausedItself(getMIDletSuite(),
261: getMIDlet().getClass().getName());
262: }
263: }
264:
265: /**
266: * Provides a <code>MIDlet</code> with a mechanism to retrieve
267: * <code>MIDletSuite</code> for this MIDlet.
268: *
269: * @return MIDletSuite for this MIDlet
270: */
271: public final MIDletSuite getMIDletSuite() {
272: return midletStateHandler.getMIDletSuite();
273: }
274:
275: /**
276: * Used by a <code>MIDlet</code> to notify the application management
277: * software that it is
278: * interested in entering the <i>ACTIVE</i> state. Calls to
279: * this method can be used by the application management software to
280: * determine which applications to move to the <i>ACTIVE</i> state.
281: * <p>
282: * When the application management software decides to activate this
283: * application it will call the <code>startApp</code> method.
284: * <p> The application is generally in the <i>PAUSED</i> state when this is
285: * called. Even in the paused state the application may handle
286: * asynchronous events such as timers or callbacks.
287: */
288:
289: public final void resumeRequest() {
290: midletStateListener.resumeRequest(getMIDletSuite(), getMIDlet()
291: .getClass().getName());
292: }
293:
294: /**
295: * Requests that the device handle (e.g. display or install)
296: * the indicated URL.
297: *
298: * <p>If the platform has the appropriate capabilities and
299: * resources available, it SHOULD bring the appropriate
300: * application to the foreground and let the user interact with
301: * the content, while keeping the MIDlet suite running in the
302: * background. If the platform does not have appropriate
303: * capabilities or resources available, it MAY wait to handle the
304: * URL request until after the MIDlet suite exits. In this case,
305: * when the requesting MIDlet suite exits, the platform MUST then
306: * bring the appropriate application to the foreground to let the
307: * user interact with the content.</p>
308: *
309: * <p>This is a non-blocking method. In addition, this method does
310: * NOT queue multiple requests. On platforms where the MIDlet
311: * suite must exit before the request is handled, the platform
312: * MUST handle only the last request made. On platforms where the
313: * MIDlet suite and the request can be handled concurrently, each
314: * request that the MIDlet suite makes MUST be passed to the
315: * platform software for handling in a timely fashion.</p>
316: *
317: * <p>If the URL specified refers to a MIDlet suite (either an
318: * Application Descriptor or a JAR file), the request is
319: * interpreted as a request to install the named package. In this
320: * case, the platform's normal MIDlet suite installation process
321: * SHOULD be used, and the user MUST be allowed to control the
322: * process (including cancelling the download and/or
323: * installation). If the MIDlet suite being installed is an
324: * <em>update</em> of the currently running MIDlet suite, the
325: * platform MUST first stop the currently running MIDlet suite
326: * before performing the update. On some platforms, the currently
327: * running MIDlet suite MAY need to be stopped before any
328: * installations can occur.</p>
329: *
330: * <p>If the URL specified is of the form
331: * <code>tel:<number></code>, as specified in <a
332: * href="http://rfc.net/rfc2806.html">RFC2806</a>, then the
333: * platform MUST interpret this as a request to initiate a voice
334: * call. The request MUST be passed to the "phone"
335: * application to handle if one is present in the platform.</p>
336: *
337: * <p>Devices MAY choose to support additional URL schemes beyond
338: * the requirements listed above.</p>
339: *
340: * <p>Many of the ways this method will be used could have a
341: * financial impact to the user (e.g. transferring data through a
342: * wireless network, or initiating a voice call). Therefore the
343: * platform MUST ask the user to explicitly acknowledge each
344: * request before the action is taken. Implementation freedoms are
345: * possible so that a pleasant user experience is retained. For
346: * example, some platforms may put up a dialog for each request
347: * asking the user for permission, while other platforms may
348: * launch the appropriate application and populate the URL or
349: * phone number fields, but not take the action until the user
350: * explicitly clicks the load or dial buttons.</p>
351: *
352: * @return true if the MIDlet suite MUST first exit before the
353: * content can be fetched.
354: *
355: * @param URL The URL for the platform to load.
356: *
357: * @exception ConnectionNotFoundException if
358: * the platform cannot handle the URL requested.
359: *
360: */
361: public final boolean platformRequest(String URL)
362: throws ConnectionNotFoundException {
363:
364: return platformRequest.dispatch(URL);
365: }
366:
367: /**
368: * Change the state and notify.
369: * Check to make sure the new state makes sense.
370: * Changes to the status are protected by the midletStateHandler.
371: * Any change to the state notifies the midletStateHandler.
372: *
373: * @param newState new state of the MIDlet
374: */
375: void setState(int newState) {
376: synchronized (midletStateHandler) {
377: setStateWithoutNotify(newState);
378: midletStateHandler.notify();
379: }
380: }
381:
382: /**
383: * Get the status of the specified permission.
384: * If no API on the device defines the specific permission
385: * requested then it must be reported as denied.
386: * If the status of the permission is not known because it might
387: * require a user interaction then it should be reported as unknown.
388: *
389: * @param permission to check if denied, allowed, or unknown.
390: * @return 0 if the permission is denied; 1 if the permission is allowed;
391: * -1 if the status is unknown
392: */
393: public int checkPermission(String permission) {
394: return getMIDletSuite().checkPermission(permission);
395: }
396:
397: /**
398: * Change the state without notifying the MIDletStateHandler.
399: * Check to make sure the new state makes sense.
400: * <p>
401: * To be called only by the MIDletStateHandler or MIDletState while holding
402: * the lock on midletStateHandler.
403: *
404: * @param newState new state of the MIDlet
405: */
406: void setStateWithoutNotify(int newState) {
407: switch (state) {
408: case DESTROYED:
409: // can't set any thing else
410: return;
411:
412: case DESTROY_PENDING:
413: if (newState != DESTROYED) {
414: // can only set DESTROYED
415: return;
416: }
417:
418: break;
419:
420: case PAUSED:
421: if (newState == PAUSE_PENDING) {
422: // already paused by app
423: return;
424: }
425:
426: break;
427:
428: case PAUSE_PENDING:
429: if (newState == ACTIVE_PENDING) {
430: /*
431: * pausedApp has not been called so the state
432: * can be set to active to cancel the pending pauseApp.
433: */
434: state = ACTIVE;
435: return;
436: }
437:
438: break;
439:
440: case ACTIVE:
441: if (newState == ACTIVE_PENDING) {
442: // already active
443: return;
444: }
445:
446: break;
447:
448: case ACTIVE_PENDING:
449: if (newState == PAUSE_PENDING) {
450: /*
451: * startApp has not been called so the state
452: * can be set to paused to cancel the pending startApp.
453: */
454: state = PAUSED;
455: return;
456: }
457:
458: break;
459: }
460:
461: state = newState;
462: }
463:
464: /**
465: * Get the state.
466: *
467: * @return current state of the MIDlet.
468: */
469: int getState() {
470: synchronized (midletStateHandler) {
471: return state;
472: }
473: }
474:
475: /**
476: * Pause a MIDlet.
477: * MIDletEventConsumer I/F method.
478: */
479: public void handleMIDletPauseEvent() {
480: setState(MIDletPeer.PAUSE_PENDING);
481: }
482:
483: /**
484: * Activate a MIDlet.
485: * MIDletEventConsumer I/F method.
486: */
487: public void handleMIDletActivateEvent() {
488: setState(MIDletPeer.ACTIVE_PENDING);
489: }
490:
491: /**
492: * Destroy a MIDlet.
493: * MIDletEventConsumer I/F method.
494: */
495: public void handleMIDletDestroyEvent() {
496: setState(MIDletPeer.DESTROY_PENDING);
497: }
498: }
|