Installerpublic class Installer extends Object An Installer manages MIDlet suites and libraries
present in a Java application environment. An MIDlet suite
distributed as a descriptor and JAR pair.
The descriptor identifies the configuration and contains security
information and the manifest of the JAR describes the contents.
The implementation of an Installer is
specific to the platform and provides access to
procedures that make an MIDlet suite visible to users.
Each installed package is uniquely identified by a storage name
constructed from the combination
of the values of the MIDlet-Name and
MIDlet-Vendor attributes.
The syntax and content of the strings used to identify
installed packages are implementation dependent.
Only packages installed or upgraded using this API appear
in the list of known packages. |
Fields Summary |
---|
public static final int | DOWNLOADING_JADStatus code to signal connection to the JAD server was successful. | public static final int | DOWNLOADED_1K_OF_JADStatus code to signal that another 1K of the JAD has been download. | public static final int | DOWNLOADING_JARStatus code to signal connection to the JAR server was successful. | public static final int | DOWNLOADED_1K_OF_JARStatus code to signal that another 1K of the JAR has been download. | public static final int | VERIFYING_SUITEStatus code to signal that download is done and the suite is being
verified. | public static final int | STORING_SUITEStatus code for local writing of the verified MIDlet suite.
Stopping the install at this point has no effect, so there user
should not be given a chance to stop the install. | public static final String | JAR_MANIFESTFilename of Manifest inside the application archive. | public static final String | DATA_SIZE_PROPMIDlet property for the size of the application data. | public static final String | JAR_SIZE_PROPMIDlet property for the size of the application archive. | public static final String | JAR_URL_PROPMIDlet property for the application archive URL. | public static final String | SUITE_NAME_PROPMIDlet property for the suite name. | public static final String | VENDOR_PROPMIDlet property for the suite vendor. | public static final String | VERSION_PROPMIDlet property for the suite version. | public static final String | DESC_PROPMIDlet property for the suite description. | public static final String | NOTIFY_PROPMIDlet property for the install notify URL. | public static final String | DELETE_PROPMIDlet property for the delete notify URL. | public static final String | CONFIGURATION_PROPMIDlet property for the microedition configuration. | public static final String | PROFILE_PROPMIDlet property for the profile. | public static final String | PERMISSIONS_PROPMIDlet property for the required permissions. | public static final String | PERMISSIONS_OPT_PROPMIDlet property for the optional permissions. | public static final String | JAD_MTMedia-Type for valid application descriptor files. | public static final String | JAR_MT_1Media-Type for valid Jar file. | public static final String | JAR_MT_2Media-Type for valid Jar file. | private static final int | MAX_DL_SIZEMax number of bytes to download at one time (1K). | private static final String | BASIC_TAGTag that signals that the HTTP server supports basic auth. | static final String | CA_FILENAMEFilename to save the domain owner of the suite. | static final String | JAD_URL_FILENAMEFilename to save the URL of the JAD. | static final String | JAR_URL_FILENAMEFilename to save the URL of the JAR. | static final String | JAD_ENCODING_FILENAMEFilename to save the encoding of the JAD. | static final String | SETTINGS_FILENAMEFilename to save the encoding of the JAD. | static final String | JAR_FILENAMEFilename to suite JAR. | static final String | JAD_FILENAMEFilename to save the suite application descriptor. | static final String | DELETE_NOTIFY_FILENAMEFilename to save the delete notification URLs. | static final String | TMP_FILENAMEFilename to save the JAR of the suite temporarily. This is used
to avoid overwriting an existing JAR prior to
verification. | static final String | MANIFEST_FILENAMEFilename to save the application manifest. | static final String | SUITE_LIST_FILENAMEFilename of the list of installed suites. | static final String | SUCCESS_MSGSuccess message for the suite provider. | static final String | INSUFFICIENT_MEM_MSGError message for the suite provider. | static final String | USER_CANCELLED_MSGError message for the suite provider. | static final String | JAR_SIZE_MISMATCH_MSGError message for the suite provider. | static final String | ATTRIBUTE_MISMATCH_MSGError message for the suite provider. | static final String | INVALID_JAD_MSGError message for the suite provider. | static final String | INVALID_JAR_MSGError message for the suite provider. | static final String | INCOMPATIBLE_MSGError message for the suite provider. | static final String | AUTHENTICATION_FAILURE_MSGError message for authentication failure. | static final String | AUTHORIZATION_FAILURE_MSGError message for authorization failure. | static final String | PUSH_REG_FAILURE_MSGError message for push registration failure. | static final String | DELETE_NOTIFICATION_MSGError message for push registration failure. | private static Installer | myInstallerPrivate reference to the singleton Installer class. | private static com.sun.midp.security.SecurityToken | classSecurityTokenThis class has a different security domain than the MIDlet suite. | private String | nextMidletSuiteToRunThe unique storage name of the next MIDlet suite to run. | private String | nextMidletToRunThe of the next MIDlet to run. | private Vector | midletSuiteListCache of installed MIDlet suites. | private Vector | deleteNotifyURLListCache of delete notification URL list. | private javax.microedition.io.HttpConnection | httpConnectionHTTP connection to close when we stop the installation. | private InputStream | httpInputStreamHTTP stream to close when we stop the installation. | protected InstallStateImpl | stateHolds the install state. | private String | cldcConfigHolds the CLDC configuration string. | private Vector | supportedProfilesHolds the MIDP supported profiles. | private String | unsignedSecurityDomainUse this to be the security domain for unsigned suites. |
Constructors Summary |
---|
protected Installer()Constructor to prevent a second instantiation of the Installer.
if (myInstaller != null) {
throw new
SecurityException("Illegal instantiation of the Installer");
}
|
Methods Summary |
---|
private void | addToDeleteNotifyURLList(java.lang.String url)Queues provider's URL to send a delete notification message
during the next OTA session.
Vector deleteNotifyList;
if (url == null) {
return;
}
// NOTE: The spec only guarantees that the http:// protocol be
// supported. We do not do any checking of the protocol.
deleteNotifyList = getDeleteNotifyURLList();
deleteNotifyList.addElement(url);
saveDeleteNotifyURLList(deleteNotifyList);
| private void | addToSuiteList(java.lang.String storageName)Adds storage name into the list of MIDlet suites.
Vector suiteList;
if (suiteExists(storageName)) {
return;
}
suiteList = getSuiteList();
suiteList.addElement(storageName);
saveSuiteList(suiteList);
| private void | applyCurrentUserLevelPermissions(byte[] current, byte[] domainPermissions, byte[] next)Apply the previous user level permission of the currently installed
version of a suite to the next version of the suite in a secure way.
for (int i = 0; i < current.length && i < next.length; i++) {
switch (current[i]) {
case Permissions.ALLOW:
case Permissions.DENY:
// not a user level permission
continue;
}
switch (domainPermissions[i]) {
case Permissions.ALLOW:
case Permissions.DENY:
// not a user level permission
continue;
case Permissions.ONE_SHOT:
if (current[i] == Permissions.SESSION ||
current[i] == Permissions.DENY_SESSION) {
// do not apply
continue;
}
// fall through
case Permissions.SESSION:
if (current[i] == Permissions.BLANKET ||
current[i] == Permissions.BLANKET_GRANTED) {
// do not apply
continue;
}
// fall through
default:
next[i] = current[i];
continue;
}
}
| private void | checkConfiguration()Checks to make sure the configration need by the application
is supported.
Send a message back to the server if the check fails and
throw an exception.
String config;
config = state.getAppProperty(CONFIGURATION_PROP);
if (config == null || config.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.MISSING_CONFIGURATION);
}
if (cldcConfig == null) {
// need to call trim to remove trailing spaces
cldcConfig =
System.getProperty("microedition.configuration").trim();
}
if (matchVersion(cldcConfig, config)) {
// success, done
return;
}
postInstallMsgBackToProvider(INCOMPATIBLE_MSG);
throw new InvalidJadException(InvalidJadException.DEVICE_INCOMPATIBLE);
| private void | checkForDifferentDomains(java.lang.String url)If the JAD belongs to an installed suite, check the URL against the
installed one. Set the state.exception if the user needs to be warned.
// perform a domain check not a straight compare
if (state.ca == null && state.previousUrl != null) {
HttpUrl old = new HttpUrl(state.previousUrl);
HttpUrl current = new HttpUrl(url);
if ((current.domain != null && old.domain == null) ||
(current.domain == null && old.domain != null) ||
(current.domain != null && old.domain != null &&
!current.domain.regionMatches(true, 0, old.domain, 0,
old.domain.length()))) {
/*
* The jad is at new location, could be bad,
* let the user decide
*/
state.exception = new InvalidJadException(
InvalidJadException.JAD_MOVED,
(state.previousUrl == null ? "none" :
state.previousUrl));
return;
}
}
| protected void | checkForJadManifestMismatches()Checks to see that if any properties that are both in the JAD and
JAR manifest are not equal and throw a exception and notify the
server when a mismatch is found. Only used for trusted suites.
for (int i = 0; i < state.jarProps.size(); i++) {
String key = state.jarProps.getKeyAt(i);
String value = state.jarProps.getValueAt(i);
String dup = state.jadProps.getProperty(key);
if (dup == null) {
continue;
}
if (!dup.equals(value)) {
postInstallMsgBackToProvider(ATTRIBUTE_MISMATCH_MSG);
throw new InvalidJadException(
InvalidJadException.ATTRIBUTE_MISMATCH, key);
}
}
| private void | checkIfBasicAuthSupported(java.lang.String wwwAuthField)Checks to make sure the HTTP server will support Basic authtication.
wwwAuthField = wwwAuthField.trim();
if (!wwwAuthField.regionMatches(true, 0, BASIC_TAG, 0,
BASIC_TAG.length())) {
throw new InvalidJadException(InvalidJadException.CANNOT_AUTH);
}
| private void | checkPreviousVersion()See if there is an installed version of the suite being installed and
if so, make an necessary checks. Will set state fields, including
the exception field for warning the user.
MIDletSuite midletSuite;
String installedVersion;
int cmpResult;
// Check if app already exists
midletSuite = getMIDletSuite(state.storageName);
try {
checkVersionFormat(state.version);
if (midletSuite == null) {
// there is no previous version
return;
}
state.isPreviousVersion = true;
state.previousSuite = midletSuite;
// get this now so we do not have to save the whole suite object
state.previousJadUrl = midletSuite.getJadUrl();
state.previousJarUrl = midletSuite.getJarUrl();
state.previousUrl = midletSuite.getDownloadUrl();
state.previousCA = midletSuite.getCA();
if (state.previousCA == null) {
state.previousCA = Resource.getString("the manufacturer");
}
if (state.force) {
// do not ask questions, force an overwrite
return;
}
// If it does, check version information
installedVersion = midletSuite.getProperty(VERSION_PROP);
cmpResult = vercmp(state.version, installedVersion);
if (cmpResult < 0) {
// older version, warn user
state.exception = new InvalidJadException(
InvalidJadException.OLD_VERSION,
installedVersion);
return;
}
if (cmpResult == 0) {
// already installed, warn user
state.exception = new InvalidJadException(
InvalidJadException.ALREADY_INSTALLED,
installedVersion);
return;
}
// new version, warn user
state.exception = new InvalidJadException(
InvalidJadException.NEW_VERSION,
installedVersion);
return;
} catch (NumberFormatException nfe) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.INVALID_VERSION);
}
| private void | checkVersionFormat(java.lang.String ver)Checks the format of a version string.
Versions must be in the form xxx.yyy.zzz, where:
xxx is the major version
yyy is the minor version
zzz is the micro version
It is acceptable to omit the micro and possibly the minor versions.
If these are not included in the version string, the period immediately
preceding the number must also be removed. So, the versions
xxx.yyy or xxx are also valid.
Version numbers do not have to be three digits wide. However, you may
pad versions with leading zeros if desired.
int length;
int start = 0;
int end;
length = ver.length();
for (int i = 0; ; i++) {
// check for more than 3 parts or a trailing '.'
if (i == 3 || start == length) {
throw new NumberFormatException();
}
end = ver.indexOf('.", start);
if (end == -1) {
end = length;
}
// throws NFE if the substring is not all digits
Integer.parseInt(ver.substring(start, end));
if (end == length) {
// we are done
return;
}
// next time around start after the index of '.'
start = end + 1;
}
| private void | cleanUp(java.lang.String storageName)Removes the resources a software package given its storage name
File file = new File(classSecurityToken);
String storageRoot = File.getStorageRoot() + storageName;
Vector files;
int numberOfFiles;
/*
* Before removing the suite, check for registered push
* connections that might need to be unregistered.
*/
PushRegistryImpl.unregisterConnections(classSecurityToken,
storageName);
files = file.filenamesThatStartWith(storageRoot);
numberOfFiles = files.size();
for (int i = 0; i < numberOfFiles; i++) {
try {
file.delete((String)files.elementAt(i));
} catch (IOException ioe) {
// ignore
}
}
| private byte[] | downloadJAD()Downloads an application descriptor file from the given URL.
String[] encoding = new String[1];
ByteArrayOutputStream bos = new ByteArrayOutputStream(MAX_DL_SIZE);
String[] acceptableTypes = {JAD_MT};
String[] extraFieldKeys = new String[3];
String[] extraFieldValues = new String[3];
String locale;
String prof = System.getProperty("microedition.profiles");
int space = prof.indexOf(' ");
if (space != -1) {
prof = prof.substring(0, space);
}
extraFieldKeys[0] = "User-Agent";
extraFieldValues[0] = "Profile/" + prof
+ " Configuration/" +
System.getProperty("microedition.configuration");
extraFieldKeys[1] = "Accept-Charset";
extraFieldValues[1] = "UTF-8, " +
System.getProperty("microedition.encoding");
/* locale can be null */
locale = System.getProperty("microedition.locale");
if (locale != null) {
extraFieldKeys[2] = "Accept-Language";
extraFieldValues[2] = locale;
}
state.beginTransferDataStatus = DOWNLOADING_JAD;
state.transferStatus = DOWNLOADED_1K_OF_JAD;
/*
* Do not send the list of acceptable types because some servers
* will send a 406 if the URL is to a JAR. It is better to
* reject the resource at the client after check the media-type so
* if the type is JAR a JAR only install can be performed.
*/
downloadResource(state.jadUrl, extraFieldKeys, extraFieldValues,
acceptableTypes, false, false, bos, encoding,
InvalidJadException.INVALID_JAD_URL,
InvalidJadException.JAD_SERVER_NOT_FOUND,
InvalidJadException.JAD_NOT_FOUND,
InvalidJadException.INVALID_JAD_TYPE);
state.jadEncoding = encoding[0];
return bos.toByteArray();
| private int | downloadJAR(java.lang.String filename)Downloads an application archive file from the given URL into the
given file. Automatically handle re-trys.
HttpUrl parsedUrl;
String url;
String[] acceptableTypes = {JAR_MT_1, JAR_MT_2};
int jarSize;
RandomAccessStream jarOutputStream = null;
OutputStream outputStream = null;
parsedUrl = new HttpUrl(state.jarUrl);
if (parsedUrl.authority == null && state.jadUrl != null) {
// relative URL, add the JAD URL as the base
try {
parsedUrl.addBaseUrl(state.jadUrl);
} catch (IOException e) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new InvalidJadException(
InvalidJadException.INVALID_JAR_URL);
}
url = parsedUrl.toString();
// The JAR URL saved to storage MUST be absolute
state.jarUrl = url;
} else {
url = state.jarUrl;
}
jarOutputStream = new RandomAccessStream(classSecurityToken);
jarOutputStream.connect(filename,
RandomAccessStream.READ_WRITE_TRUNCATE);
outputStream = jarOutputStream.openOutputStream();
try {
state.beginTransferDataStatus = DOWNLOADING_JAR;
state.transferStatus = DOWNLOADED_1K_OF_JAR;
jarSize = downloadResource(url, null, null,
acceptableTypes, true, true, outputStream, null,
InvalidJadException.INVALID_JAR_URL,
InvalidJadException.JAR_SERVER_NOT_FOUND,
InvalidJadException.JAR_NOT_FOUND,
InvalidJadException.INVALID_JAR_TYPE);
return jarSize;
} catch (InvalidJadException ije) {
switch (ije.getReason()) {
case InvalidJadException.JAR_SERVER_NOT_FOUND:
case InvalidJadException.JAR_NOT_FOUND:
postInstallMsgBackToProvider(INVALID_JAD_MSG);
break;
case InvalidJadException.INVALID_JAR_TYPE:
postInstallMsgBackToProvider(INVALID_JAR_MSG);
}
throw ije;
} finally {
try {
jarOutputStream.disconnect();
} catch (Exception e) {
// ignore
}
}
| private int | downloadResource(java.lang.String url, java.lang.String[] extraFieldKeys, java.lang.String[] extraFieldValues, java.lang.String[] acceptableTypes, boolean sendAcceptableTypes, boolean allowNoMediaType, java.io.OutputStream output, java.lang.String[] encoding, int invalidURLCode, int serverNotFoundCode, int resourceNotFoundCode, int invalidMediaTypeCode)Downloads an resource from the given URL into the output stream.
Connection conn = null;
StringBuffer acceptField;
int responseCode;
String retryAfterField;
int retryInterval;
String mediaType;
try {
for (; ; ) {
try {
conn = Connector.open(url, Connector.READ);
} catch (IllegalArgumentException e) {
throw new InvalidJadException(invalidURLCode, url);
} catch (ConnectionNotFoundException e) {
// protcol not found
throw new InvalidJadException(invalidURLCode, url);
}
if (!(conn instanceof HttpConnection)) {
// only HTTP or HTTPS are supported
throw new InvalidJadException(invalidURLCode, url);
}
httpConnection = (HttpConnection)conn;
if (extraFieldKeys != null) {
for (int i = 0; i < extraFieldKeys.length &&
extraFieldKeys[i] != null; i++) {
httpConnection.setRequestProperty(extraFieldKeys[i],
extraFieldValues[i]);
}
}
// 256 is given to avoid resizing without adding lengths
acceptField = new StringBuffer(256);
if (sendAcceptableTypes) {
// there must be one or more acceptable media types
acceptField.append(acceptableTypes[0]);
for (int i = 1; i < acceptableTypes.length; i++) {
acceptField.append(", ");
acceptField.append(acceptableTypes[i]);
}
httpConnection.setRequestProperty("Accept",
acceptField.toString());
}
httpConnection.setRequestMethod(HttpConnection.GET);
if (state.username != null &&
state.password != null) {
httpConnection.setRequestProperty("Authorization",
formatAuthCredentials(state.username,
state.password));
}
if (state.proxyUsername != null &&
state.proxyPassword != null) {
httpConnection.setRequestProperty("Proxy-Authorization",
formatAuthCredentials(state.proxyUsername,
state.proxyPassword));
}
try {
responseCode = httpConnection.getResponseCode();
} catch (IOException ioe) {
if (httpConnection.getHost() == null) {
throw new InvalidJadException(invalidURLCode, url);
}
throw new InvalidJadException(serverNotFoundCode, url);
}
if (responseCode != HttpConnection.HTTP_UNAVAILABLE) {
break;
}
retryAfterField = httpConnection.getHeaderField("Retry-After");
if (retryAfterField == null) {
break;
}
try {
/*
* see if the retry interval is in seconds, and
* not an absolute date
*/
retryInterval = Integer.parseInt(retryAfterField);
if (retryInterval > 0) {
if (retryInterval > 60) {
// only wait 1 min
retryInterval = 60;
}
Thread.sleep(retryInterval * 1000);
}
} catch (InterruptedException ie) {
// ignore thread interrupt
break;
} catch (NumberFormatException ne) {
// ignore bad format
break;
}
httpConnection.close();
if (state.stopInstallation) {
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
}
} // end for
if (responseCode == HttpConnection.HTTP_NOT_FOUND) {
throw new InvalidJadException(resourceNotFoundCode);
}
if (responseCode == HttpConnection.HTTP_NOT_ACCEPTABLE) {
throw new InvalidJadException(invalidMediaTypeCode, "");
}
if (responseCode == HttpConnection.HTTP_UNAUTHORIZED) {
// automatically throws the correct exception
checkIfBasicAuthSupported(
httpConnection.getHeaderField("WWW-Authenticate"));
state.exception =
new InvalidJadException(InvalidJadException.UNAUTHORIZED);
return 0;
}
if (responseCode == HttpConnection.HTTP_PROXY_AUTH) {
// automatically throws the correct exception
checkIfBasicAuthSupported(
httpConnection.getHeaderField("WWW-Authenticate"));
state.exception =
new InvalidJadException(InvalidJadException.PROXY_AUTH);
return 0;
}
if (responseCode != HttpConnection.HTTP_OK) {
throw new IOException("Failed to download " + url +
" HTTP response code: " + responseCode);
}
mediaType = getMediaType(httpConnection.getType());
if (mediaType != null) {
boolean goodType = false;
for (int i = 0; i < acceptableTypes.length; i++) {
if (mediaType.equals(acceptableTypes[i])) {
goodType = true;
break;
}
}
if (!goodType) {
throw new InvalidJadException(invalidMediaTypeCode,
mediaType);
}
} else if (!allowNoMediaType) {
throw new InvalidJadException(invalidMediaTypeCode, "");
}
if (encoding != null) {
encoding[0] = getCharset(httpConnection.getType());
}
httpInputStream = httpConnection.openInputStream();
return transferData(httpInputStream, output);
} finally {
// Close the streams or connections this method opened.
try {
httpInputStream.close();
} catch (Exception e) {
// ignore
}
try {
conn.close();
} catch (Exception e) {
// ignore
}
}
| public boolean | execute(java.lang.String storageName, java.lang.String midlet)Queues the execution of the named Application suite to run.
The current application suite should terminate itself normally
to make resources available to the new application suite. Only
one package and set of MIDlets can be queued in this manner.
If multiple calls to execute are made, the package and MIDlets
specified during the last invokation will be executed
when the current application is terminated.
nextMidletSuiteToRun = storageName;
nextMidletToRun = midlet;
return true;
| private static java.lang.String | formatAuthCredentials(java.lang.String username, java.lang.String password)Formats the username and password for HTTP basic authentication
according RFC 2617.
byte[] data = new byte[username.length() + password.length() + 1];
int j = 0;
for (int i = 0; i < username.length(); i++, j++) {
data[j] = (byte)username.charAt(i);
}
data[j] = (byte)':";
j++;
for (int i = 0; i < password.length(); i++, j++) {
data[j] = (byte)password.charAt(i);
}
return "Basic " + Base64.encode(data, 0, data.length);
| java.lang.String | getCA(java.lang.String storageRoot)Gets the CA of an installed package.
RandomAccessStream myStorage;
DataInputStream dataStream;
String ca;
myStorage = new RandomAccessStream(classSecurityToken);
try {
myStorage.connect(storageRoot + CA_FILENAME,
Connector.READ);
try {
dataStream = myStorage.openDataInputStream();
ca = dataStream.readUTF();
} finally {
myStorage.disconnect();
}
return ca;
} catch (IOException ioe) {
return null;
}
| private static java.lang.String | getCharset(java.lang.String contentType)Parses out the charset from the content-type field.
The charset parameter is after the ';' in the content-type field.
int start;
int end;
if (contentType == null) {
return null;
}
start = contentType.indexOf("charset");
if (start < 0) {
return null;
}
start = contentType.indexOf('=", start);
if (start < 0) {
return null;
}
// start past the '='
start++;
end = contentType.indexOf(';", start);
if (end < 0) {
end = contentType.length();
}
return contentType.substring(start, end).trim();
| private java.util.Vector | getDeleteNotifyURLList()Retrieves the queued delete notification list. Each element
in the Vector is the URL to send the delete notification to.
RandomAccessStream storage;
DataInputStream in;
String line;
if (deleteNotifyURLList != null) {
return deleteNotifyURLList;
}
deleteNotifyURLList = new Vector();
try {
storage = new RandomAccessStream(classSecurityToken);
storage.connect(File.getStorageRoot() + DELETE_NOTIFY_FILENAME,
Connector.READ);
try {
in = storage.openDataInputStream();
for (; ; ) {
line = in.readUTF();
if (line == null) {
break;
}
deleteNotifyURLList.addElement(line);
}
} finally {
storage.disconnect();
}
} catch (IOException e) {
/* will happen the first time, because it will not exist */
}
return deleteNotifyURLList;
| protected byte[][] | getInitialPermissions(java.lang.String domain)Builds the initial API permission for suite currently being installed.
byte[][] domainPermissions = Permissions.forDomain(classSecurityToken,
domain);
byte[] permissions = Permissions.getEmptySet();
// only the current level of each permission has to be adjusted
getRequestedPermissions(PERMISSIONS_PROP,
domainPermissions[Permissions.CUR_LEVELS],
permissions, true);
getRequestedPermissions(PERMISSIONS_OPT_PROP,
domainPermissions[Permissions.CUR_LEVELS],
permissions, false);
domainPermissions[Permissions.CUR_LEVELS] = permissions;
return domainPermissions;
| public static com.sun.midp.midletsuite.Installer | getInstaller()Returns a reference to the singleton Installer object.
MIDletSuite midletSuite = Scheduler.getScheduler().getMIDletSuite();
// if a MIDlet suite is not scheduled, assume the JAM is calling.
if (midletSuite != null) {
midletSuite.checkIfPermissionAllowed(Permissions.AMS);
}
return getInstallerCommon();
| public static com.sun.midp.midletsuite.Installer | getInstaller(com.sun.midp.security.SecurityToken securityToken)Returns a reference to the singleton Installer object.
securityToken.checkIfPermissionAllowed(Permissions.AMS);
return getInstallerCommon();
| private static com.sun.midp.midletsuite.Installer | getInstallerCommon()Returns a reference to the singleton Installer object.
String nameOfInstaller;
if (myInstaller != null) {
return myInstaller;
}
/*
* In internal.config an alternate installer, such as a
* SecureInstaller can be specified.
*/
nameOfInstaller = Configuration.getProperty(
"com.sun.midp.midletsuite.installer");
if (nameOfInstaller == null) {
myInstaller = new SecureInstaller();
return myInstaller;
}
try {
myInstaller =
(Installer)Class.forName(nameOfInstaller).newInstance();
return myInstaller;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
| public com.sun.midp.midlet.MIDletSuite | getMIDletSuite(java.lang.String storageName)Gets the MIDlet Suite from storage.
return getMIDletSuite(storageName, null);
| public com.sun.midp.midlet.MIDletSuite | getMIDletSuite(java.lang.String storageName, java.lang.String midletToRun)Gets the MIDlet Suite from storage, and selects one midlet to be run.
File file = new File(classSecurityToken);
String storageRoot = File.getStorageRoot() + storageName;
String ca;
if (!suiteExists(storageName)) {
return null;
}
ca = getCA(storageRoot);
return new MIDletSuiteImpl(classSecurityToken,
storageRoot,
storageName,
ca,
midletToRun);
| public com.sun.midp.midlet.MIDletSuite | getMIDletSuite(java.lang.String storageName, int midletToRun)Gets the MIDlet Suite from storage, and selects one midlet to be run.
File file = new File(classSecurityToken);
String storageRoot = File.getStorageRoot() + storageName;
String ca;
if (!suiteExists(storageName)) {
return null;
}
ca = getCA(storageRoot);
return new MIDletSuiteImpl(classSecurityToken,
storageRoot,
storageName,
ca,
midletToRun);
| private static java.lang.String | getMediaType(java.lang.String contentType)Parses out the media-type from the content-type field and convert
to lower case.
The media-type everything for the ';' that marks the parameters.
int semiColon;
if (contentType == null) {
return null;
}
semiColon = contentType.indexOf(';");
if (semiColon < 0) {
return contentType.toLowerCase();
}
return contentType.substring(0, semiColon).toLowerCase();
| public java.lang.String | getNextMIDletSuiteToRun()Gets the unique storage name of the next MIDlet suite to run.
return nextMidletSuiteToRun;
| public java.lang.String | getNextMIDletToRun()Gets the name of the next MIDlet to run.
return nextMidletToRun;
| private void | getRequestedPermissions(java.lang.String propName, byte[] domainPermissions, byte[] permissions, boolean required)Gets the permissions for a domain that are requested the manifest.
String reqPermissionLine;
Vector manifestPermissions;
String permission;
boolean found;
int i;
reqPermissionLine = state.getAppProperty(propName);
if (reqPermissionLine == null) {
return;
}
manifestPermissions = Util.getCommaSeparatedValues(reqPermissionLine);
if (manifestPermissions.size() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.INVALID_VALUE);
}
for (int j = 0; j < manifestPermissions.size(); j++) {
permission = (String)manifestPermissions.elementAt(j);
if (permission.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.INVALID_VALUE);
}
found = false;
for (i = 0; i < Permissions.NUMBER_OF_PERMISSIONS; i++) {
if (Permissions.getName(i).equals(permission)) {
if (domainPermissions[i] != Permissions.NEVER) {
found = true;
}
break;
}
}
if (!found) {
if (required) {
postInstallMsgBackToProvider(AUTHORIZATION_FAILURE_MSG);
throw new InvalidJadException(
InvalidJadException.AUTHORIZATION_FAILURE, permission);
}
continue;
}
permissions[i] = domainPermissions[i];
}
| protected java.lang.String | getSecurityDomainName(java.lang.String storageName, java.lang.String ca)Gets the security domain name for this MIDlet Suite from storage.
// This is a method that is overridden by a secure installer.
return Permissions.UNTRUSTED_DOMAIN_NAME;
| private java.util.Vector | getSuiteList()Retrieves the list of MIDlet suites and store them into a Vector
object. Each element in the Vector is the storage name
of an installed application.
RandomAccessStream storage;
DataInputStream in;
String line;
if (midletSuiteList != null) {
return midletSuiteList;
}
midletSuiteList = new Vector();
try {
storage = new RandomAccessStream(classSecurityToken);
storage.connect(File.getStorageRoot() +
SUITE_LIST_FILENAME, Connector.READ);
try {
in = storage.openDataInputStream();
for (; ; ) {
line = in.readUTF();
if (line == null) {
break;
}
midletSuiteList.addElement(line);
}
} finally {
storage.disconnect();
}
} catch (IOException e) {
/* will happen the first time, because it will not exist */
}
return midletSuiteList;
| public static void | initSecurityToken(com.sun.midp.security.SecurityToken token)Initializes the security token for this class, so it can
perform actions that a normal MIDlet Suite cannot.
if (classSecurityToken != null) {
return;
}
classSecurityToken = token;
MIDletSuiteImpl.initSecurityToken(classSecurityToken);
| public java.lang.String | installJad(java.lang.String location, boolean force, boolean removeRMS, InstallListener installListener)Installs a software package from the given URL. The URL is assumed
refer to an application descriptor.
If the component to be installed is the same as an existing
component (by comparing the MIDlet-Name ,
MIDlet-Vendor attributes)
then this install is an upgrade if the version number is greater
than the current version. If so, the new version replaces in its
entirety the current version.
It is implementation dependent when the upgraded component is
made available for use.
The implementation of install must be robust in the presence
of failures such as running out of memory. If this method
throws an exception then the package must not be installed
and any previous version of the component must be left intact
and operational.
To receive status updates and installer warnings, provide an install
listener. If no listener is provided all warnings will be thrown
as exceptions.
state = new InstallStateImpl();
state.jadUrl = location;
state.force = force;
state.removeRMS = removeRMS;
state.nextStep = 1;
state.listener = installListener;
return performInstall();
| public java.lang.String | installJar(java.lang.String location, boolean force, boolean removeRMS, InstallListener installListener)Installs a software package from the given URL. The URL is assumed
refer to a JAR.
If the component to be installed is the same as an existing
component (by comparing the MIDlet-Name ,
MIDlet-Vendor attributes)
then this install is an upgrade if the version number is greater
than the current version. If so, the new version replaces in its
entirety the current version.
It is implementation dependent when the upgraded component is
made available for use.
The implementation of install must be robust in the presence
of failures such as running out of memory. If this method
throws an exception then the package must not be installed
and any previous version of the component must be left intact
and operational.
To receive status updates and installer warnings, provide an install
listener. If no listener is provided all warnings will be thrown
as exceptions.
if (location == null || location.length() == 0) {
throw
new IllegalArgumentException("Must specify URL of .jar file");
}
state = new InstallStateImpl();
state.jarUrl = location;
state.force = force;
state.removeRMS = removeRMS;
state.nextStep = 4;
state.listener = installListener;
return performInstall();
| private void | installStep1()Downloads the JAD, save it in the install state.
Parse the JAD, make sure it has
the required properties, and save them in the install state.
if (state.jadUrl == null || state.jadUrl.length() == 0) {
throw
new IllegalArgumentException("Must specify URL of .jad file");
}
state.jad = downloadJAD();
if (state.exception != null) {
return;
}
state.jadProps = new JadProperties();
try {
state.jadProps.load(new ByteArrayInputStream(state.jad),
state.jadEncoding);
} catch (InvalidJadException ije) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw ije;
}
state.suiteName = state.jadProps.getProperty(SUITE_NAME_PROP);
if (state.suiteName == null || state.suiteName.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_SUITE_NAME);
}
state.vendor = state.jadProps.getProperty(VENDOR_PROP);
if (state.vendor == null || state.vendor.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_VENDOR);
}
state.version = state.jadProps.getProperty(VERSION_PROP);
if (state.version == null || state.version.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_VERSION);
}
state.storageName = makeStorageName(state.vendor, state.suiteName);
checkPreviousVersion();
state.nextStep++;
| private void | installStep2()If the JAD belongs to an installed suite, check the URL against the
installed one.
state.nextStep++;
if (state.isPreviousVersion) {
checkForDifferentDomains(state.jadUrl);
}
| private void | installStep3()Makes sure the suite can fit in storage and confirm installation
with the user.
String sizeString;
int dataSize;
int suiteSize;
sizeString = state.jadProps.getProperty(JAR_SIZE_PROP);
if (sizeString == null) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_JAR_SIZE);
}
try {
state.expectedJarSize = Integer.parseInt(sizeString);
} catch (NumberFormatException e) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.INVALID_VALUE);
}
sizeString = state.jadProps.getProperty(DATA_SIZE_PROP);
if (sizeString == null) {
dataSize = 0;
} else {
try {
dataSize = Integer.parseInt(sizeString);
} catch (NumberFormatException e) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.INVALID_VALUE);
}
}
/*
* A suite is a jad + jar + manifest + url + data size.
* lets say the manifest is the same size as the jad
* since we do know at this point. the size is in bytes,
* UTF-8 chars can be upto 3 bytes
*/
suiteSize = state.expectedJarSize + (state.jad.length * 2) +
(state.jadUrl.length() * 3) + dataSize;
state.file = new File(classSecurityToken);
if (suiteSize > state.file.getBytesAvailableForFiles()) {
postInstallMsgBackToProvider(INSUFFICIENT_MEM_MSG);
// the size reported to the user should be in K and rounded up
throw new
InvalidJadException(InvalidJadException.INSUFFICIENT_STORAGE,
Integer.toString((suiteSize + 1023)/ 1024));
}
state.jarUrl = state.jadProps.getProperty(JAR_URL_PROP);
if (state.jarUrl == null || state.jarUrl.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAD_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_JAR_URL);
}
if (state.listener != null &&
!state.listener.confirmJarDownload(state)) {
state.stopInstallation = true;
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
}
state.nextStep++;
| private void | installStep4()Downloads the JAR, make sure it is the correct size, make sure
the required attributes match the JAD's. Then store the
application.
int bytesDownloaded;
MIDletInfo midletInfo;
String midlet;
String classfilename;
// Save jar file to temp name; we need to do this to read the
// manifest entry, but, don't want to overwrite an exisiting
// application in case there are problems with the manifest
state.storageRoot = File.getStorageRoot();
state.tempFilename = state.storageRoot + TMP_FILENAME;
bytesDownloaded = downloadJAR(state.tempFilename);
if (state.exception != null) {
return;
}
if (state.listener != null) {
state.listener.updateStatus(VERIFYING_SUITE, state);
}
try {
state.storage = new RandomAccessStream(classSecurityToken);
verifyJar(state.storage, state.tempFilename);
// Create JAR Properties (From .jar file's MANIFEST
try {
state.manifest = JarReader.readJarEntry(classSecurityToken,
state.tempFilename, JAR_MANIFEST);
if (state.manifest == null) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new
InvalidJadException(InvalidJadException.CORRUPT_JAR,
JAR_MANIFEST);
}
} catch (IOException ioe) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new
InvalidJadException(InvalidJadException.CORRUPT_JAR,
JAR_MANIFEST);
}
state.jarProps = new ManifestProperties();
try {
state.jarProps.load(new ByteArrayInputStream(state.manifest));
} catch (InvalidJadException ije) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw ije;
}
for (int i = 1; ; i++) {
midlet = state.getAppProperty("MIDlet-" + i);
if (midlet == null) {
break;
}
midletInfo = new MIDletInfo(midlet);
classfilename = midletInfo.classname.replace('.", '/") +
".class";
try {
/* Attempt to read the MIDlet from the JAR file. */
if (JarReader.readJarEntry(classSecurityToken,
state.tempFilename, classfilename) == null) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.CORRUPT_JAR, classfilename);
}
} catch (IOException ioe) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new
InvalidJadException(InvalidJadException.CORRUPT_JAR,
classfilename);
}
}
// move on to the next step after a warning
state.nextStep++;
// Check Manifest entries against .jad file
if (state.jadUrl != null) {
if (bytesDownloaded != state.expectedJarSize) {
postInstallMsgBackToProvider(JAR_SIZE_MISMATCH_MSG);
throw new InvalidJadException(
InvalidJadException.JAR_SIZE_MISMATCH);
}
if (!state.suiteName.equals(
state.jarProps.getProperty(SUITE_NAME_PROP))) {
postInstallMsgBackToProvider(ATTRIBUTE_MISMATCH_MSG);
throw new InvalidJadException(
InvalidJadException.SUITE_NAME_MISMATCH);
}
if (!state.version.equals(
state.jarProps.getProperty(VERSION_PROP))) {
postInstallMsgBackToProvider(ATTRIBUTE_MISMATCH_MSG);
throw new InvalidJadException(
InvalidJadException.VERSION_MISMATCH);
}
if (!state.vendor.equals(
state.jarProps.getProperty(VENDOR_PROP))) {
postInstallMsgBackToProvider(ATTRIBUTE_MISMATCH_MSG);
throw new InvalidJadException(
InvalidJadException.VENDOR_MISMATCH);
}
} else {
state.file = new File(classSecurityToken);
state.suiteName = state.jarProps.getProperty(SUITE_NAME_PROP);
if (state.suiteName == null ||
state.suiteName.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.MISSING_SUITE_NAME);
}
state.vendor = state.jarProps.getProperty(VENDOR_PROP);
if (state.vendor == null || state.vendor.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.MISSING_VENDOR);
}
state.version = state.jarProps.getProperty(VERSION_PROP);
if (state.version == null || state.version.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.MISSING_VERSION);
}
try {
checkVersionFormat(state.version);
} catch (NumberFormatException nfe) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new InvalidJadException(
InvalidJadException.INVALID_VERSION);
}
// NYI if already installed, check the domain of the JAR URL
state.storageName = makeStorageName(state.vendor,
state.suiteName);
checkPreviousVersion();
}
} catch (Exception e) {
state.file.delete(state.tempFilename);
if (e instanceof IOException) {
throw (IOException)e;
}
throw (RuntimeException)e;
}
| private void | installStep5()If the JAR belongs to an installed suite if there was
no JAD, check the URL against the installed one.
state.nextStep++;
if (state.jadUrl == null && state.isPreviousVersion) {
checkForDifferentDomains(state.jarUrl);
}
| private void | installStep6()Checks the permissions and store the suite.
String suiteStorageRoot;
DataOutputStream storageStream;
String securityDomain;
try {
if (state.ca != null) {
// suite was signed
securityDomain = getSecurityDomainName(state.storageRoot,
state.ca);
} else {
securityDomain = unsignedSecurityDomain;
}
/*
* untrusted and test domains do not check for requested
* permissions
*/
if (Permissions.UNTRUSTED_DOMAIN_NAME.equals(securityDomain)) {
if (state.isPreviousVersion &&
state.previousSuite.isTrusted()) {
// Do not overwrite trusted suites with untrusted ones
postInstallMsgBackToProvider(AUTHORIZATION_FAILURE_MSG);
throw new InvalidJadException(
InvalidJadException.TRUSTED_OVERWRITE_FAILURE,
state.previousCA);
}
state.permissions = Permissions.forDomain(classSecurityToken,
securityDomain);
/*
* To keep public key management simple, there is only one
* trusted keystore. So it is possible that the CA for
* the suite is untrusted. This may be done on purpose for
* testing. This is OK, but do not confuse the user by saying
* the untrusted suite is authorized, so set the CA name to
* null.
*/
state.ca = null;
} else {
/*
* For trusted suites, make sure an properties duplicated in
* both the manifest and JAD are the same.
*/
if (state.jadUrl != null) {
checkForJadManifestMismatches();
}
state.trusted = true;
state.permissions = getInitialPermissions(securityDomain);
}
if (state.isPreviousVersion) {
applyCurrentUserLevelPermissions(
state.previousSuite.getPermissions()[Permissions.CUR_LEVELS],
state.permissions[Permissions.MAX_LEVELS],
state.permissions[Permissions.CUR_LEVELS]);
if (state.removeRMS) {
// override suite comparisons, just remove RMS
RecordStoreFile.removeRecordStoresForSuite(
classSecurityToken,
state.storageRoot + state.storageName);
} else {
processPreviousRMS();
}
}
state.securityToken = new SecurityToken(classSecurityToken,
state.permissions);
checkConfiguration();
matchProfile();
suiteStorageRoot = state.storageRoot + state.storageName;
// make sure at least 1 second has passed
while (System.currentTimeMillis() - state.startTime < 1000);
synchronized (state) {
// this is the point of no return, one last check
if (state.stopInstallation) {
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
}
if (state.listener != null) {
state.listener.updateStatus(STORING_SUITE, state);
}
state.writingSuite = true;
}
registerPushConnections();
} catch (Exception e) {
state.file.delete(state.tempFilename);
if (e instanceof IOException) {
throw (IOException)e;
}
throw (RuntimeException)e;
}
try {
state.file.rename(state.tempFilename,
suiteStorageRoot + JAR_FILENAME);
state.storage.connect(suiteStorageRoot + MANIFEST_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
state.storage.writeBytes(state.manifest, 0, state.manifest.length);
state.storage.disconnect();
if (state.file.exists(suiteStorageRoot + JAD_URL_FILENAME)) {
// erase the old JAD URL since we did not get new one
state.file.delete(suiteStorageRoot + JAD_URL_FILENAME);
state.file.delete(suiteStorageRoot + JAD_FILENAME);
}
if (state.jadUrl != null) {
// Save JAD, JAR & manifest files
state.storage.connect(suiteStorageRoot + JAD_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
state.storage.writeBytes(state.jad, 0, state.jad.length);
state.storage.disconnect();
// convert the JAD URL to UTF8 and write it to storage
state.storage.connect(suiteStorageRoot + JAD_URL_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
storageStream = state.storage.openDataOutputStream();
storageStream.writeUTF(state.jadUrl);
storageStream.close();
state.storage.disconnect();
}
// convert the JAR URL to UTF8 and write it to storage
state.storage.connect(suiteStorageRoot + JAR_URL_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
storageStream = state.storage.openDataOutputStream();
storageStream.writeUTF(state.jarUrl);
storageStream.close();
state.storage.disconnect();
if (state.jadEncoding != null) {
// convert the JAD encoding to UTF8 and write it to storage
state.storage.connect(suiteStorageRoot + JAD_ENCODING_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
storageStream = state.storage.openDataOutputStream();
storageStream.writeUTF(state.jadEncoding);
storageStream.close();
state.storage.disconnect();
} else if (state.file.exists(suiteStorageRoot +
JAD_ENCODING_FILENAME)) {
// erase the old encoding since we did not get new one
state.file.delete(suiteStorageRoot + JAD_ENCODING_FILENAME);
}
// todo get the jad encoding and write to a file
if (state.ca != null) {
// convert the domain owner to UTF8 and write it to storage
state.storage.connect(suiteStorageRoot + CA_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
storageStream = state.storage.openDataOutputStream();
storageStream.writeUTF(state.ca);
storageStream.close();
state.storage.disconnect();
}
saveSuiteSettings(classSecurityToken, suiteStorageRoot,
state.pushInterruptSetting, state.permissions, state.trusted);
addToSuiteList(state.storageName);
state.nextStep++;
try {
postInstallMsgBackToProvider(SUCCESS_MSG);
} catch (RuntimeException re) {
/*
* The suite is successfully installed, but the post of the
* status message failed. Do not let this failure prevent
* the suite from being used.
*/
}
return;
} catch (Exception e) {
try {
state.storage.disconnect();
} catch (IOException ex) {
// ignore
}
cleanUp(state.storageName);
if (e instanceof IOException) {
throw (IOException)e;
}
throw (RuntimeException)e;
}
| public java.lang.String[] | list()List all installed software packages by storage name.
Vector suiteList = getSuiteList();
String[] array = new String[suiteList.size()];
for (int i = 0; i < suiteList.size(); i++) {
array[i] = (String)suiteList.elementAt(i);
}
return array;
| public static java.lang.String | makeStorageName(java.lang.String vendorName, java.lang.String appName)Returns a storage identifier of an application.
return File.unicodeToAsciiFilename(vendorName) + '_" +
File.unicodeToAsciiFilename(appName) + '_";
| private void | matchProfile()Tries to match one of the supported profiles with a profile
listed in string of profiles separated by a space.
Send a message back to the server if a match is not found and
throw an exception.
String profiles = state.getAppProperty(PROFILE_PROP);
if (profiles == null || profiles.length() == 0) {
postInstallMsgBackToProvider(INVALID_JAR_MSG);
throw new
InvalidJadException(InvalidJadException.MISSING_PROFILE);
}
// build the list of supported profiles if needed
if (supportedProfiles == null) {
int start;
int nextSpace = -1;
// need to call trim to remove trailing spaces
String meProfiles =
System.getProperty("microedition.profiles").trim();
supportedProfiles = new Vector();
for (; ; ) {
start = nextSpace + 1;
nextSpace = meProfiles.indexOf(' ", start);
// consecutive spaces, keep searching
if (nextSpace == start) {
continue;
}
if ((nextSpace < 0)) {
supportedProfiles.addElement(
meProfiles.substring(start, meProfiles.length()));
break;
}
supportedProfiles.addElement(
meProfiles.substring(start, nextSpace));
}
}
/*
* for each profiles listed in MicroEdition-Profile, we need to
* find a matching profile in microedition.profiles.
*/
int current = 0;
int nextSeparatorIndex = 0;
String requestedProfile;
boolean supported = false;
// convert tab to space so that the parsing later is simplified
StringBuffer tmp = new StringBuffer(profiles);
boolean modified = false;
while ((nextSeparatorIndex = profiles.indexOf('\t", current)) != -1) {
tmp.setCharAt(nextSeparatorIndex, ' ");
current++;
modified = true;
}
if (modified) {
profiles = tmp.toString();
}
// reset the indices
current = nextSeparatorIndex = 0;
do {
// get the next requested profiles
nextSeparatorIndex = profiles.indexOf(' ", current);
if (nextSeparatorIndex == current) {
// consecutive spaces, keep searching
current++;
continue;
}
if (nextSeparatorIndex == -1) {
// last (or the only one) value in the list
requestedProfile =
profiles.substring(current, profiles.length());
} else {
requestedProfile =
profiles.substring(current, nextSeparatorIndex);
current = nextSeparatorIndex + 1;
}
/*
* try to match each requested profiles against the supported
* ones.
*/
supported = false;
for (int i = 0; i < supportedProfiles.size(); i++) {
String supportedProfile =
(String)supportedProfiles.elementAt(i);
if (matchVersion(supportedProfile, requestedProfile)) {
supported = true;
break;
}
}
// short circuit the test if there is one mismatch
if (!supported) {
break;
}
} while (nextSeparatorIndex != -1);
// matched all requested profiles against supported ones
if (supported) {
return;
}
postInstallMsgBackToProvider(INCOMPATIBLE_MSG);
throw new InvalidJadException(InvalidJadException.DEVICE_INCOMPATIBLE);
| private boolean | matchVersion(java.lang.String name1, java.lang.String name2)Match the name of the configuration or profile, and return
true if the first name has a greater or equal version than the
second. The names of the format "XXX-Y.Y" (e.g. CLDC-1.0, MIDP-2.0)
as used in the system properties (microedition.configuration &
microedition.profiles).
This is used for checking both configuration and profiles.
String base1 = name1.substring(0, name1.indexOf('-"));
String base2 = name2.substring(0, name1.indexOf('-"));
if (base1.equals(base2)) {
String ver1 = name1.substring(name1.indexOf('-") + 1,
name1.length());
String ver2 = name2.substring(name2.indexOf('-") + 1,
name2.length());
return (vercmp(ver1, ver2) >= 0);
} else {
return false;
}
| private synchronized java.lang.String | performInstall()Performs an install.
/* Disable push interruptions during install. */
PushRegistryImpl.enablePushLaunch(classSecurityToken, false);
try {
state.startTime = System.currentTimeMillis();
while (state.nextStep < 7) {
/*
* clear the previous warning, so we can tell if another has
* happened
*/
state.exception = null;
if (state.stopInstallation) {
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
}
switch (state.nextStep) {
case 1:
installStep1();
break;
case 2:
installStep2();
break;
case 3:
installStep3();
break;
case 4:
installStep4();
break;
case 5:
installStep5();
break;
case 6:
installStep6();
break;
}
if (state.exception != null) {
if (state.listener == null) {
throw state.exception;
}
if (!state.listener.warnUser(state)) {
state.stopInstallation = true;
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw state.exception;
}
}
}
} finally {
PushRegistryImpl.enablePushLaunch(classSecurityToken, true);
}
return state.storageName;
| private void | postInstallMsgBackToProvider(java.lang.String message)Posts a status message back to the provider's URL in JAD.
String url;
// Send out delete notifications that have been queued, first
postQueuedDeleteMsgsBackToProvider();
// Now, send out install notifications
url = state.getAppProperty(NOTIFY_PROP);
postMsgBackToProvider(message, url);
| private void | postMsgBackToProvider(java.lang.String message, java.lang.String url)Posts a status message back to the provider's URL in JAD.
HttpConnection transaction;
OutputStream out;
if (url == null) {
return;
}
try {
transaction = (HttpConnection)Connector.open(url, Connector.WRITE);
try {
transaction.setRequestMethod(HttpConnection.POST);
if ((state != null) &&
(state.proxyUsername != null) &&
(state.proxyPassword != null)) {
transaction.setRequestProperty("Proxy-Authorization",
formatAuthCredentials(state.proxyUsername,
state.proxyPassword));
}
out = transaction.openOutputStream();
try {
out.write(message.getBytes());
} finally {
out.close();
}
// we will wait for the server to run the script
transaction.getResponseCode();
} finally {
transaction.close();
}
} catch (IOException ioe) {
// ignore IOExceptions
} catch (IllegalArgumentException iae) {
// ignore IllegalArgumentExceptions
} catch (ClassCastException cce) {
// ignore ClassCastExceptions
}
| private void | postQueuedDeleteMsgsBackToProvider()Posts all queued delete notification messages
Vector deleteNotifyList;
Enumeration e;
deleteNotifyList = getDeleteNotifyURLList();
for (e = deleteNotifyList.elements(); e.hasMoreElements(); ) {
String url = (String)e.nextElement();
postMsgBackToProvider(DELETE_NOTIFICATION_MSG, url);
removeFromDeleteNotifyURLList(url);
}
| private void | processPreviousRMS()If this is an update, make sure the RMS data is handle correctly
according to the OTA spec.
From the OTA spec:
The RMS record stores of a MIDlet suite being updated MUST be
managed as follows:
-
If the cryptographic signer of the new MIDlet suite and the
original MIDlet suite are identical, then the RMS record
stores MUST be retained and made available to the new MIDlet
suite.
-
If the scheme, host, and path of the URL that the new
Application Descriptor is downloaded from is identical to the
scheme, host, and path of the URL the original Application
Descriptor was downloaded from, then the RMS MUST be retained
and made available to the new MIDlet suite.
-
If the scheme, host, and path of the URL that the new MIDlet
suite is downloaded from is identical to the scheme, host, and
path of the URL the original MIDlet suite was downloaded from,
then the RMS MUST be retained and made available to the new
MIDlet suite.
-
If the above statements are false, then the device MUST ask
the user whether the data from the original MIDlet suite
should be retained and made available to the new MIDlet
suite.
HttpUrl newUrl;
HttpUrl originalUrl;
String suiteStorageRoot = state.storageRoot + state.storageName;
if (!RecordStoreFile.suiteHasRmsData(suiteStorageRoot)) {
return;
}
if (state.previousCA != null && state.ca != null &&
state.ca.equals(state.previousCA)) {
// signers the same
return;
}
try {
newUrl = new HttpUrl(state.jadUrl);
originalUrl = new HttpUrl(state.previousJadUrl);
if (newUrl.scheme.equals(originalUrl.scheme) &&
newUrl.host.equals(originalUrl.host) &&
newUrl.path.equals(originalUrl.path)) {
return;
}
} catch (NullPointerException npe) {
// no match, fall through
}
try {
newUrl = new HttpUrl(state.jarUrl);
originalUrl = new HttpUrl(state.previousJarUrl);
if (newUrl.scheme.equals(originalUrl.scheme) &&
newUrl.host.equals(originalUrl.host) &&
newUrl.path.equals(originalUrl.path)) {
return;
}
} catch (NullPointerException npe) {
// no match, fall through
}
// ask the user, if no listener assume no for user's answer
if (state.listener != null) {
if (state.listener.keepRMS(state)) {
// user wants to keep the data
return;
}
}
// this is a good place to check for a stop installing call
if (state.stopInstallation) {
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
}
RecordStoreFile.removeRecordStoresForSuite(classSecurityToken,
suiteStorageRoot);
| private void | redoPreviousPushConnections()Registers the push connections for previous version after
and aborted update.
for (int i = 1; ; i++) {
String pushProp;
pushProp = state.previousSuite.getProperty("MIDlet-Push-" + i);
if (pushProp == null) {
break;
}
/*
* Parse the comma separated values -
* " connection, midlet, role, filter"
*/
int comma1 = pushProp.indexOf(',", 0);
int comma2 = pushProp.indexOf(',", comma1 + 1);
String conn = pushProp.substring(0, comma1).trim();
String midlet = pushProp.substring(comma1+1, comma2).trim();
String filter = pushProp.substring(comma2+1).trim();
/* Register the new push connection string. */
try {
PushRegistryImpl.registerConnectionInternal(
classSecurityToken,
state,
conn, midlet,
filter, true);
} catch (IOException e) {
// should not happen
} catch (ClassNotFoundException e) {
// should not happen
}
}
| private void | registerPushConnections()Registers the push connections for the application.
Send a message back to the server if a connection cannot be
registered and throw an exception.
byte[] curLevels = state.permissions[Permissions.CUR_LEVELS];
if (state.isPreviousVersion) {
PushRegistryImpl.unregisterConnections(classSecurityToken,
state.storageName);
}
for (int i = 1; ; i++) {
String pushProp;
pushProp = state.getAppProperty("MIDlet-Push-" + i);
if (pushProp == null) {
break;
}
/*
* Parse the comma separated values -
* " connection, midlet, role, filter"
*/
int comma1 = pushProp.indexOf(',", 0);
int comma2 = pushProp.indexOf(',", comma1 + 1);
String conn = pushProp.substring(0, comma1).trim();
String midlet = pushProp.substring(comma1+1, comma2).trim();
String filter = pushProp.substring(comma2+1).trim();
/* Register the new push connection string. */
try {
PushRegistryImpl.registerConnectionInternal(
classSecurityToken,
state,
conn, midlet,
filter, false);
} catch (Exception e) {
/* If already registered, abort the installation. */
PushRegistryImpl.unregisterConnections(classSecurityToken,
state.storageName);
if (state.isPreviousVersion) {
// put back the old ones, removed above
redoPreviousPushConnections();
}
if (e instanceof SecurityException) {
postInstallMsgBackToProvider(AUTHORIZATION_FAILURE_MSG);
// since our state object put the permission in message
throw new InvalidJadException(
InvalidJadException.AUTHORIZATION_FAILURE,
e.getMessage());
}
postInstallMsgBackToProvider(PUSH_REG_FAILURE_MSG);
if (e instanceof IllegalArgumentException) {
throw new InvalidJadException(
InvalidJadException.PUSH_FORMAT_FAILURE, pushProp);
}
if (e instanceof ConnectionNotFoundException) {
throw new InvalidJadException(
InvalidJadException.PUSH_PROTO_FAILURE, pushProp);
}
if (e instanceof IOException) {
throw new InvalidJadException(
InvalidJadException.PUSH_DUP_FAILURE, pushProp);
}
if (e instanceof ClassNotFoundException) {
throw new InvalidJadException(
InvalidJadException.PUSH_CLASS_FAILURE, pushProp);
}
// error in the RI code
throw (RuntimeException)e;
}
}
if (state.isPreviousVersion) {
// use the old setting
state.pushInterruptSetting =
(byte)state.previousSuite.getPushInterruptSetting();
// The old suite may have not had push connections
if (state.pushInterruptSetting != Permissions.NEVER) {
return;
}
}
if (curLevels[Permissions.PUSH] == Permissions.NEVER) {
state.pushInterruptSetting = Permissions.NEVER;
} else if (curLevels[Permissions.PUSH] == Permissions.ALLOW) {
state.pushInterruptSetting = Permissions.BLANKET;
} else if (curLevels[Permissions.PUSH] == Permissions.ONE_SHOT) {
// treat the ONE_SHOT as sesssion for interrupting
state.pushInterruptSetting = Permissions.SESSION;
} else {
state.pushInterruptSetting = curLevels[Permissions.PUSH];
}
| public void | remove(java.lang.String storageName)Removes a software package given its storage name
If the component is in use it must continue to be available
to the other components that are using it. The resources it
consumes must not be released until it is not in use.
MIDletSuite suiteToDelete = getMIDletSuite(storageName, null);
String deleteURL = null;
synchronized (myInstaller) {
/*
* Block any push launch operations while the remove is
* in progress.
*/
PushRegistryImpl.enablePushLaunch(classSecurityToken, false);
try {
/*
* Before removing the suite, get info to send delete
* notification back to server.
*/
suiteToDelete = getMIDletSuite(storageName, null);
if (suiteToDelete != null) {
deleteURL = suiteToDelete.getProperty(DELETE_PROP);
addToDeleteNotifyURLList(deleteURL);
}
if (!removeFromSuiteList(storageName)) {
throw new IllegalArgumentException("Could not find " +
storageName);
}
cleanUp(storageName);
} finally {
PushRegistryImpl.enablePushLaunch(classSecurityToken, true);
}
}
| private void | removeFromDeleteNotifyURLList(java.lang.String url)Removes the URL from the delete notification list.
Vector deleteNotifyList;
deleteNotifyList = getDeleteNotifyURLList();
deleteNotifyList.removeElement(url);
saveDeleteNotifyURLList(deleteNotifyList);
| private boolean | removeFromSuiteList(java.lang.String storageName)Removes the suite from the list of installed suites.
Vector suiteList;
if (!suiteExists(storageName)) {
return false;
}
suiteList = getSuiteList();
suiteList.removeElement(storageName);
saveSuiteList(suiteList);
return true;
| private void | saveDeleteNotifyURLList(java.util.Vector list)Writes the given delete notification list to disk.
File file;
RandomAccessStream storage;
String filename;
DataOutputStream out;
String line;
file = new File(classSecurityToken);
storage = new RandomAccessStream(classSecurityToken);
filename = File.getStorageRoot() + DELETE_NOTIFY_FILENAME;
try {
file.delete(filename);
} catch (Exception e) {
// Ignore the exception that happens when the file does not exist
}
try {
storage.connect(filename, RandomAccessStream.READ_WRITE_TRUNCATE);
try {
out = storage.openDataOutputStream();
for (int i = 0; i < list.size(); i++) {
line = (String)list.elementAt(i);
out.writeUTF(line);
}
} finally {
storage.disconnect();
}
} catch (IOException e) {
/* Ignored. Should not happen. */
}
| private void | saveSuiteList(java.util.Vector list)Writes the given MIDlet suite list to disk.
File file;
RandomAccessStream storage;
String filename;
DataOutputStream out;
String line;
file = new File(classSecurityToken);
storage = new RandomAccessStream(classSecurityToken);
filename = File.getStorageRoot() + SUITE_LIST_FILENAME;
try {
file.delete(filename);
} catch (Exception e) {
// ignore the exception that happen when the file does not exist
}
try {
storage.connect(filename, RandomAccessStream.READ_WRITE_TRUNCATE);
try {
out = storage.openDataOutputStream();
for (int i = 0; i < list.size(); i++) {
line = (String)list.elementAt(i);
out.writeUTF(line);
}
} finally {
storage.disconnect();
}
} catch (IOException e) {
/* ignored. Shouldn't happen */
}
| public static void | saveSuiteSettings(com.sun.midp.security.SecurityToken token, java.lang.String suiteStorageRoot, byte pushInterruptSetting, byte[][] permissions, boolean trusted)Saves any of the settings (security or others) that the user may have
changed.
saveSuiteSettings(new RandomAccessStream(token),
suiteStorageRoot, pushInterruptSetting, permissions, trusted);
| public static void | saveSuiteSettings(java.lang.String suiteStorageRoot, byte pushInterruptSetting, byte[][] permissions, boolean trusted)Saves any of the settings (security or others) that the user may have
changed.
saveSuiteSettings(new RandomAccessStream(), suiteStorageRoot,
pushInterruptSetting, permissions, trusted);
| private static void | saveSuiteSettings(com.sun.midp.io.j2me.storage.RandomAccessStream storage, java.lang.String suiteStorageRoot, byte pushInterruptSetting, byte[][] permissions, boolean trusted)Saves any of the settings (security or others) that the user may have
changed.
DataOutputStream storageStream;
byte[] maximums = permissions[Permissions.MAX_LEVELS];
byte[] currentLevels = permissions[Permissions.CUR_LEVELS];
storage.connect(suiteStorageRoot + SETTINGS_FILENAME,
RandomAccessStream.READ_WRITE_TRUNCATE);
try {
storageStream = storage.openDataOutputStream();
/*
* write the version of the file
* Note that version number only has to
* increase if data has been removed, not if new data has been
* added to the end of the file.
*/
storageStream.writeByte(1);
storageStream.writeBoolean(trusted);
storageStream.writeByte(pushInterruptSetting);
storageStream.writeByte(currentLevels.length);
for (int i = 0; i < currentLevels.length; i++) {
storageStream.writeByte(currentLevels[i]);
}
storageStream.writeByte(maximums.length);
for (int i = 0; i < maximums.length; i++) {
storageStream.writeByte(maximums[i]);
}
// Add new items at the end of the file
} finally {
try {
storage.disconnect();
} catch (IOException e) {
// ignore
}
}
| public void | setUnsignedSecurityDomain(java.lang.String domain)Sets security domain for unsigned suites. The default is untrusted.
Can only be called by JAM for testing.
MIDletSuite midletSuite = Scheduler.getScheduler().getMIDletSuite();
// if a MIDlet suite is not scheduled, assume the JAM is calling.
if (midletSuite != null) {
midletSuite.checkIfPermissionAllowed(Permissions.MIDP);
}
unsignedSecurityDomain = domain;
| public boolean | stopInstalling()Stops the installation. If installer is not installing then this
method has no effect. This will cause the install method to
throw an IOException if the install is not writting the suite
to storage which is the point of no return.
if (state == null) {
return false;
}
synchronized (state) {
if (state.writingSuite) {
return false;
}
state.stopInstallation = true;
try {
httpInputStream.close();
} catch (Exception e) {
// ignore
}
try {
httpConnection.close();
} catch (Exception e) {
// ignore
}
return true;
}
| public boolean | suiteExists(java.lang.String storageName)Tells if a suite exists.
Vector suiteList;
suiteList = getSuiteList();
for (int i = 0; i < suiteList.size(); i++) {
if (suiteList.elementAt(i).equals(storageName)) {
return true;
}
}
return false;
| private int | transferData(java.io.InputStream in, java.io.OutputStream out)Function that actually does the work of transfering file data.
Updates the listener ever 1 K bytes.
If the amount of data to be read is larger than maxDLSize
we will break the input into chunks no larger than
maxDLSize . This prevents the VM from running out of
memory when processing large files.
byte[] buffer = new byte[MAX_DL_SIZE];
int bytesRead;
int totalBytesWritten = 0;
if (state.listener != null) {
state.listener.updateStatus(state.beginTransferDataStatus, state);
}
try {
for (int nextUpdate = totalBytesWritten + 1024; ; ) {
bytesRead = in.read(buffer);
if (state.listener != null && (bytesRead == -1 ||
totalBytesWritten + bytesRead >= nextUpdate)) {
synchronized (state) {
if (state.stopInstallation) {
throw new IOException("stopped");
}
state.listener.updateStatus(state.transferStatus,
state);
}
nextUpdate = totalBytesWritten + 1024;
}
if (bytesRead == -1) {
return totalBytesWritten;
}
out.write(buffer, 0, bytesRead);
totalBytesWritten += bytesRead;
}
} catch (IOException ioe) {
if (state.stopInstallation) {
postInstallMsgBackToProvider(USER_CANCELLED_MSG);
throw new IOException("stopped");
} else {
throw ioe;
}
}
| private int | vercmp(java.lang.String ver1, java.lang.String ver2)Compares two version strings. The return values are very similar to
that of strcmp() in 'C'. If the first version is less than the second
version, a negative number will be returned. If the first version is
greater than the second version, a positive number will be returned.
If the two versions are equal, zero is returned.
Versions must be in the form xxx.yyy.zzz, where:
xxx is the major version
yyy is the minor version
zzz is the micro version
It is acceptable to omit the micro and possibly the minor versions.
If these are not included in the version string, the period immediately
preceding the number must also be removed. So, the versions
xxx.yyy or xxx are also valid.
Version numbers do not have to be three digits wide. However, you may
pad versions with leading zeros if desired.
If a version number is omitted, its value is assumed to be zero. All
tests will be based on this assumption.
For example:
1.04 > 1.
1.04 < 1.4.1
1.04 = 1.4.0
String strVal1;
String strVal2;
int intVal1;
int intVal2;
int idx1 = 0;
int idx2 = 0;
int newidx;
if ((ver1 == null) && (ver2 == null)) {
return 0;
}
if (ver1 == null) {
return -1;
}
if (ver2 == null) {
return 1;
}
for (int i = 0; i < 3; i++) {
strVal1 = "0"; // Default value
strVal2 = "0"; // Default value
if (idx1 >= 0) {
newidx = ver1.indexOf('.", idx1);
if (newidx < 0) {
strVal1 = ver1.substring(idx1);
} else {
strVal1 = ver1.substring(idx1, newidx);
newidx++; // Idx of '.'; need to go to next char
}
idx1 = newidx;
}
if (idx2 >= 0) {
newidx = ver2.indexOf('.", idx2);
if (newidx < 0) {
strVal2 = ver2.substring(idx2);
} else {
strVal2 = ver2.substring(idx2, newidx);
newidx++;
}
idx2 = newidx;
}
intVal1 = Integer.parseInt(strVal1); // May throw NFE
intVal2 = Integer.parseInt(strVal2); // May throw NFE
if (intVal1 > intVal2) {
return 1;
}
if (intVal1 < intVal2) {
return -1;
}
}
return 0;
| protected void | verifyJar(com.sun.midp.io.j2me.storage.RandomAccessStream jarStorage, java.lang.String jarFilename)Verifies a Jar. Post any error back to the server.
| public boolean | wasStopped()Tells if the installation was stopped by another thread.
if (state == null) {
return false;
}
return state.stopInstallation;
|
|