/*
*
*
* 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.main;
import com.sun.midp.security.SecurityToken;
import com.sun.midp.io.j2me.serversocket.Socket;
import com.sun.midp.midlet.MIDletSuite;
import com.sun.midp.events.EventTypes;
import com.sun.midp.events.EventQueue;
import com.sun.midp.events.EventListener;
import com.sun.midp.events.Event;
import com.sun.midp.events.NativeEvent;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Vector;
import javax.microedition.io.ServerSocketConnection;
import javax.microedition.io.SocketConnection;
/**
* A service for testing the Native AMS (nams). Listens on a socket and
* processes commands by calling native methods that invoke the native AMS
* API. Receives Native AMS callbacks and sends the information out through
* the socket.
*/
public class NamsTestService implements EventListener, Runnable {
static final int PORT = 13322;
static final String PFX = "namstestsvc: ";
/**
* Prints string s to stdout, prefixed by PFX.
*/
static void d(String s) {
System.out.print(PFX + s + "\n");
}
/**
* Initializes the nams service. Sets up translation of native callbacks
* into events, opens up a socket, and creates the service.
*/
public static void init(SecurityToken token, EventQueue eq) {
ServerSocketConnection serv;
initialize(MIDletSuiteUtils.getIsolateId());
try {
Socket s = new Socket();
s.open(PORT, token);
serv = (ServerSocketConnection) s;
} catch (Throwable t) {
d("failed " + t.toString());
t.printStackTrace();
return;
}
new NamsTestService(serv, eq);
}
ServerSocketConnection serv;
DataInputStream in;
PrintStream out;
boolean reading;
/**
* Constructor for the test service. Starts the listener thread and
* registers itself as a listener for test events.
*/
NamsTestService(ServerSocketConnection s, EventQueue eq) {
serv = s;
new Thread(this).start();
eq.registerEventListener(EventTypes.TEST_EVENT, this);
}
/**
* The socket listener loop. Awaits a connection, processes requests from
* the connection, then goes back to waiting, forever.
*/
public void run() {
SocketConnection sock;
while (true) {
d("listening on port " + PORT);
try {
sock = (SocketConnection)serv.acceptAndOpen();
} catch (IOException ioe) {
d("accept failed: " + ioe);
try {
serv.close();
} catch (IOException ignored) { }
return;
}
d("connected");
readSocket(sock);
d("disconnected");
}
}
/**
* Opens input and output streams from a fresh connection, then processes
* input from the socket. Closes and cleans up after input processing
* completes.
*/
void readSocket(SocketConnection sock) {
try {
in = sock.openDataInputStream();
} catch (IOException ioe) {
d("input stream failed: " + ioe);
try {
sock.close();
} catch (IOException ignored) { }
return;
}
try {
out = new PrintStream(sock.openDataOutputStream());
} catch (IOException ioe) {
d("output stream failed: " + ioe);
try {
in.close();
} catch (IOException ignored) { }
in = null;
try {
sock.close();
} catch (IOException ignored) { }
return;
}
out.println("> NAMS Test Service ready.");
readLines();
out.close();
out = null;
try {
in.close();
} catch (IOException ignored) { }
in = null;
try {
sock.close();
} catch (IOException ignored) { }
}
/**
* Reads lines of input from the socket and processes them. Reads as long
* as the 'reading' boolean is true, then returns.
*/
void readLines() {
StringBuffer sb = new StringBuffer(100);
reading = true;
while (reading) {
int b;
try {
b = in.read();
} catch (IOException ioe) {
break;
}
if (b == -1 && sb.length() == 0) {
break;
}
if (b == -1 || b == '\n') {
int len = sb.length();
if (len > 0 && sb.charAt(len-1) == '\r') {
sb.setLength(len-1);
}
processLine(tokenize(sb.toString()));
sb.setLength(0);
} else {
sb.append((char)(b & 0xff));
}
}
}
/**
* Tokenizes a string in a simple fashion. Given a line of input, returns
* an array of strings containing tokens. A token consists of a sequence
* of nonblank characters. Tokens are separated by one or more blanks.
*/
String[] tokenize(String st) {
Vector vec = new Vector();
int start = -1;
int cur = 0;
int len = st.length();
while (true) {
while (cur < len && st.charAt(cur) == ' ') {
cur += 1;
}
start = cur;
while (cur < len && st.charAt(cur) != ' ') {
cur += 1;
}
if (start >= len) {
break;
}
vec.addElement(st.substring(start, cur));
}
String[] arr = new String[vec.size()];
vec.copyInto(arr);
return arr;
}
/**
* A simple argument checking function. Given an argument array argv,
* ensures that it is exactly rqd elements long, and then attempts to
* parse argument idx as an integer. If all of these are successful, the
* parsed integer value is returned in an Integer object. Otherwise, null
* is returned.
*/
Integer check(String[] argv, int rqd, int idx) {
if (argv.length != rqd) {
reply("> ?");
return null;
}
try {
return Integer.valueOf(argv[idx]);
} catch (NumberFormatException nfe) {
reply("> ?");
return null;
}
}
void echo(String[] argv, String pfx, int argStart) {
String s = pfx + argv[argStart];
for (int i = argStart+1; i < argv.length; i++) {
s += " " + argv[i];
}
reply(s);
}
void reply(String s) {
d(s);
// IMPL_NOTE not thread-safe
// IMPL_NOTE if no connection, should buffer up events
// and send them all out when a connection arrives
if (out != null) {
out.println(s);
}
}
/**
* Processes a command and its arguments.
*/
void processLine(String[] sa) {
if (sa.length == 0) {
return;
}
echo(sa, "< ", 0);
if ("quit".equals(sa[0])) {
reading = false;
} else if ("echo".equals(sa[0])) {
// no need to do anything
// if (sa.length > 1) {
// echo(sa, "> ", 1);
// }
} else if ("createstart".equals(sa[0])) {
Integer app = check(sa, 4, 3);
if (app != null) {
int suiteId = MIDletSuite.UNUSED_SUITE_ID;
try {
suiteId = Integer.parseInt(sa[1]);
} catch (NumberFormatException nfe) {
// Intentionally ignored
}
NamsAPIWrapper.midletCreateStart(suiteId,
sa[2],
app.intValue());
}
} else if ("resume".equals(sa[0])) {
Integer app = check(sa, 2, 1);
if (app != null) {
NamsAPIWrapper.midletResume(app.intValue());
}
} else if ("pause".equals(sa[0])) {
Integer app = check(sa, 2, 1);
if (app != null) {
NamsAPIWrapper.midletPause(app.intValue());
}
} else if ("destroy".equals(sa[0])) {
Integer app = check(sa, 3, 1);
if (app != null) {
int timeout = -1;
try {
timeout = Integer.parseInt(sa[2]);
} catch (NumberFormatException nfe) {
// Intentionally ignored
}
NamsAPIWrapper.midletDestroy(app.intValue(),
timeout);
}
} else if ("setfg".equals(sa[0])) {
Integer app = check(sa, 2, 1);
if (app != null) {
NamsAPIWrapper.midletSetForeground(app.intValue());
}
} else if ("stop".equals(sa[0])) {
NamsAPIWrapper.midpSystemStop();
} else if ("help".equals(sa[0])) {
for (int ii = 0; ii < helpmsg.length; ii++) {
reply("> " + helpmsg[ii]);
}
} else {
reply("> ?");
}
}
/**
* Help message strings.
*/
String helpmsg[] = {
"createstart suite-name class-name app-id",
"destroy app-id timeout",
"echo [args ...]",
"pause app-id",
"quit",
"resume app-id",
"setfg app-id",
"stop"
};
// -------------------- interface EventListener --------------------
/**
* Preprocesses events. Does no preprocessing, so always returns true.
*/
public boolean preprocess(Event event, Event waitingEvent) {
return true;
}
/**
* Processes test events. Decodes the event and sends the information out
* through the socket.
*/
public void process(Event event) {
NativeEvent e = (NativeEvent)event;
int appId = e.intParam1;
int callbackId = e.intParam2;
String state = getStateByValue(e.intParam3);
int reason = e.intParam4;
String s;
switch (callbackId) {
case 0: // background
reply("> background, appId = " + appId + ", state = " + state +
", reason = " + reason);
break;
case 1: // foreground
reply("> foreground, appId = " + appId + ", state = " + state +
", reason = " + reason);
case 2: // state change
reply("> state, appId = " + appId + ", state = " + state +
", reason = " + reason);
break;
default:
reply(
"> callbackId=" + callbackId +
" appId=" + appId +
" state=" + state +
" reason=" + reason);
break;
}
}
/**
* Converts a midlet state (including foreground/background states) value
* into readable string.
*
* @param state state value to convert
*
* @return a string representing the given state value
*/
private String getStateByValue(int state) {
/* IMPL_NOTE: see midpNativeAppManager.h for the definitions */
final String[] stateStrings = {
"MIDP_MIDLET_STATE_ACTIVE",
"MIDP_MIDLET_STATE_PAUSED",
"MIDP_MIDLET_STATE_DESTROYED",
"MIDP_MIDLET_STATE_ERROR",
"MIDP_DISPLAY_STATE_FOREGROUND",
"MIDP_DISPLAY_STATE_BACKGROUND",
"MIDP_DISPLAY_STATE_FOREGROUND_REQUEST",
"MIDP_DISPLAY_STATE_BACKGROUND_REQUEST"
};
if (state >= 1 && state < stateStrings.length) {
return stateStrings[state - 1];
}
return "unknown (" + state + ")";
}
// -------------------- natives --------------------
/**
* Initializes the native portion of the NAMS Test Service.
*
* @param isolateId the isolateId to which test events are sent
*/
private native static void initialize(int isolateId);
/**
* Cleans up the native portion of the NAMS Test Service.
*/
private native static void cleanup();
}
|