javamanager/javainstaller/installer/javasrc/com/nokia/mj/impl/installer/midp2/install/steps/ConfirmInstallation.java
/*
* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.mj.impl.installer.midp2.install.steps;
import com.nokia.mj.impl.installer.customisationproperties.CustomisationProperties;
import com.nokia.mj.impl.installer.exetable.ExeBall;
import com.nokia.mj.impl.installer.exetable.ExeStep;
import com.nokia.mj.impl.installer.ui.ApplicationInfo;
import com.nokia.mj.impl.installer.ui.InstallInfo;
import com.nokia.mj.impl.installer.utils.DriveInfo;
import com.nokia.mj.impl.installer.utils.FileUtils;
import com.nokia.mj.impl.installer.utils.InstallerException;
import com.nokia.mj.impl.installer.utils.Log;
import com.nokia.mj.impl.installer.utils.Platform;
import com.nokia.mj.impl.installer.utils.SysUtil;
import com.nokia.mj.impl.security.midp.common.SigningCertificate;
import com.nokia.mj.impl.security.midp.authentication.AuthenticationModule;
import java.io.InputStream;
import java.io.IOException;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
/**
* ConfirmInstallation shows installation confirmation dialog to user.
*/
public class ConfirmInstallation extends ExeStep
{
public void execute(ExeBall aBall)
{
InstallBall ball = (InstallBall)aBall;
// Get possible certificates for the application.
SigningCertificate[] certs = AuthenticationModule.getInstance()
.getCertificatesDetails(ball.iStorageHandler.getSession(),
ball.iSuite.getUid());
if (certs != null)
{
Log.log("Possible certificates for this application:");
for (int i = 0; i < certs.length; i++)
{
Log.log("Certificate[" + i + "]:\n" + certs[i]);
}
}
// Check if RMS data should be retained in case of an
// upgrade as specified in MIDP 2.1 spec chapter
// 2.1.5 "MIDlet Suite Update".
if (ball.iOldSuite != null)
{
// Check from signer if private data should
// be retained in application upgrade.
checkSameSignerForUpgradeData(ball, ball.iOldCertificates, certs);
// Check from jad and jar urls if private data should
// be retained in application upgrade.
checkUrlsForUpgradeData(ball);
}
if (ball.isSilent() || Platform.isLinux())
{
// Silent installation, assume positive answer from user.
// Linux does not have UI, assume positive answer from user.
ball.iUserConfirmation = new Boolean(true);
return;
}
// Collect all the info needed in confirmation dialog,
// display it to user and based on user response
// initialise ball.iUserConfirmation and other data.
// Use localized suite name if it exists
String suiteName = ball.getAttributeValue("Nokia-MIDlet-Localized-Name");
if (suiteName == null)
{
suiteName = ball.iSuite.getName();
}
InstallInfo installInfo = new InstallInfo(ball.iSuite.getUid(),
suiteName, ball.iSuite.getVendor(),
ball.iSuite.getVersion().toString());
// In case of an update, set old suite version.
if (ball.iOldSuite != null)
{
installInfo.setOldVersion(ball.iOldSuite.getVersion().toString());
}
// Set Jar size and data size from Jad/Manifest attributes.
installInfo.setJarSize(getAttributeLongValue(ball, "MIDlet-Jar-Size"));
installInfo.setDataSize(getAttributeLongValue(ball, "MIDlet-Data-Size"));
if (installInfo.getJarSize() <= 0 &&
ball.iJarFilename != null && ball.iDownloader == null)
{
// Get jar file size.
installInfo.setJarSize(FileUtils.getSize(ball.iJarFilename));
}
// Set certificates.
installInfo.setCertificates(certs);
// Set ApplicationInfos.
installInfo.setApplications(getApplicationInfos(ball));
// Set RetainData flag if this is an application update and
// retain user data decision has not been made yet.
if (ball.iOldSuite != null && ball.iUpgradeData == null)
{
installInfo.setRetainData(true);
}
// Set installation drive.
installInfo.setDriveId(ball.iInstallationDrive);
Vector drives = new Vector();
SysUtil.getUserVisibleDrives(drives);
removeFullDrives(ball, drives);
int[] driveIds = new int[drives.size()];
int[] driveTypes = new int[drives.size()];
long[] driveFreeSpaces = new long[drives.size()];
for (int i = 0; i < drives.size(); i++)
{
DriveInfo driveInfo = (DriveInfo)drives.elementAt(i);
// Note that this trusts that DriveInfo constant values
// are identical to drive type constants in InstallInfo.
driveIds[i] = driveInfo.getNumber();
driveTypes[i] = driveInfo.getDriveType();
driveFreeSpaces[i] = driveInfo.getFreeSpace();
}
installInfo.setDriveIdsTypes(driveIds, driveTypes);
installInfo.setDriveFreeSpaces(driveFreeSpaces);
// Set icon paths.
JarFile jarFile = null;
InputStream iconInputStream = null;
if (ball.iJarFilename != null && ball.iDownloader == null)
{
// We have jar filename and are not downloading it,
// so jar file must be available.
// Get icon InputStream for correct icon in jar file.
String suiteIconPath = ball.iSuite.getAttributeValue("MIDlet-Icon");
if (suiteIconPath == null)
{
suiteIconPath = ball.iSuite.getFirstIconPath();
}
Log.log("SuiteIconPath for the UI resource: " + suiteIconPath);
if (suiteIconPath != null)
{
try
{
jarFile = new JarFile(ball.iJarFilename);
iconInputStream = jarFile.getInputStream
(new JarEntry(FileUtils.trimJarEntry(suiteIconPath)));
installInfo.setIconInputStream(iconInputStream);
installInfo.setIconPath(suiteIconPath);
}
catch (IOException ioe)
{
Log.logWarning("Getting InputStream for icon failed", ioe);
}
}
}
// Ask user confirmation.
boolean userConfirmation = true;
try
{
if (ball.getInstallerUi() != null)
{
userConfirmation = ball.getInstallerUi().confirm(installInfo);
}
}
catch (Throwable t)
{
InstallerException.internalError(
"Exception from InstallerUi.confirm: " + t, t);
userConfirmation = false;
}
if (!userConfirmation)
{
ball.cancel();
}
ball.iUserConfirmation = new Boolean(userConfirmation);
// Close the jarFile and iconInputStream.
if (jarFile != null)
{
try
{
jarFile.close(); // Closes also input stream
jarFile = null;
}
catch (IOException ioe)
{
Log.logWarning("Closing icon InputStream failed", ioe);
}
}
// Now that user has answered to confirmation dialog, take into
// use user selections: driveId and retainData.
int oldInstallationDrive = ball.iInstallationDrive;
ball.iInstallationDrive = installInfo.getDriveId();
if (ball.iUpgradeData == null)
{
// Get RetainData flag.
ball.iUpgradeData = installInfo.getRetainData();
}
Log.log("UserConfirmation: " + ball.iUserConfirmation);
Log.log("InstallationDrive: " + ball.iInstallationDrive);
Log.log("UpgradeData: " + ball.iUpgradeData);
if (oldInstallationDrive != ball.iInstallationDrive)
{
try
{
// Save user selection.
SysUtil.setRepositoryStringValue(
SysUtil.REPO_ID_JAVA_INST_VARIATION,
SysUtil.REPO_KEY_JAVA_INST_DEF_INST_DRIVE,
FileUtils.getDriveName(ball.iInstallationDrive));
Log.log("Updated user chosen drive to repository: " +
ball.iInstallationDrive);
}
catch (Throwable t)
{
Log.log("Updating user chosen drive to repository failed", t);
}
}
// Log all suite info.
//ball.log(ball.iSuite.toString());
}
public void cancel(ExeBall aBall)
{
// nop
}
/**
* Get ApplicationInfos to be passed to InstallerUi.
*/
protected static ApplicationInfo[] getApplicationInfos(InstallBall aBall)
{
Vector appInfos = aBall.iSuite.getApplications();
ApplicationInfo[] result = null;
if (appInfos != null)
{
result = new ApplicationInfo[appInfos.size()];
for (int i = 0; i < result.length; i++)
{
com.nokia.mj.impl.installer.storagehandler.ApplicationInfo
appInfo =
(com.nokia.mj.impl.installer.storagehandler.ApplicationInfo)
appInfos.elementAt(i);
// Use localized application names if defined
String attrName = "Nokia-MIDlet-Localized-" + (i + 1);
String midletName = aBall.getAttributeValue(attrName);
if (midletName == null)
{
midletName = appInfo.getName();
}
result[i] = new ApplicationInfo(appInfo.getUid(),
midletName,
appInfo.getIconPath());
}
}
return result;
}
/**
* Checks from certificate signers if the application private data
* needs to be retained during application upgrade.
*/
private void checkSameSignerForUpgradeData(InstallBall aBall,
SigningCertificate[] aOldCerts,
SigningCertificate[] aNewCerts)
{
if (aBall.iUpgradeData != null)
{
// iUpgradeData flag has already been set.
return;
}
if (aOldCerts == null && aNewCerts != null)
{
// Old and new suites have different signers, do not set
// iUpgradeData flag.
return;
}
if (aOldCerts != null && aNewCerts != null)
{
for (int i = 0;
i < aOldCerts.length && aBall.iUpgradeData == null;
i++)
{
for (int j = 0;
j < aNewCerts.length && aBall.iUpgradeData == null;
j++)
{
if (aOldCerts[i].isSameSigner(aNewCerts[j]))
{
// Old and new suites have the same signer
// so data must be retained.
Log.log("Retain user data, same signer for old and new suite: " +
aNewCerts[j].getOrganization());
aBall.iUpgradeData = new Boolean(true);
}
}
}
}
}
/**
* Checks from jad and jar urls if the application private data
* needs to be retained during application upgrade.
*/
private void checkUrlsForUpgradeData(InstallBall aBall)
{
if (aBall.iUpgradeData != null)
{
// iUpgradeData flag has already been set.
return;
}
if (aBall.iOldSuite != null)
{
checkUrlsForUpgradeData(aBall,
aBall.iOldSuite.getJadUrl(),
aBall.iJadUrl);
checkUrlsForUpgradeData(aBall,
aBall.iOldSuite.getJarUrl(),
aBall.iJarUrl);
}
}
/**
* Checks from given urls if the application private data
* needs to be retained during application upgrade.
*/
private void checkUrlsForUpgradeData
(InstallBall aBall, String aOldUrl, String aNewUrl)
{
if (aBall.iUpgradeData == null)
{
// Upgrade data selection has not been made yet,
// check from given urls.
if (aOldUrl != null && aNewUrl != null && aOldUrl.equals(aNewUrl))
{
// Old and new urls are identical so data must be retained.
Log.log("Retain user data, same url for old and new suite: " +
aNewUrl);
aBall.iUpgradeData = new Boolean(true);
}
}
}
/**
* Get long type Jad/Manifest attribute value, or -1 if attribute
* is not found.
*/
private long getAttributeLongValue(InstallBall aBall, String aAttrName)
{
String attrValue = aBall.getAttributeValue(aAttrName);
long size = -1;
if (attrValue != null)
{
try
{
size = Long.parseLong(attrValue);
}
catch (NumberFormatException nfe)
{
Log.logWarning("Parsing " + aAttrName + " value " +
attrValue + " failed", nfe);
}
}
return size;
}
/**
* Removes drives which do not have enough free space for the
* application from the aDrives vector.
*/
private void removeFullDrives(InstallBall aBall, Vector aDrives)
{
int requiredSize = CheckDiskSpace.getRequiredSize(aBall);
for (int i = 0; i < aDrives.size(); i++)
{
DriveInfo drive = (DriveInfo)aDrives.elementAt(i);
int driveId = drive.getNumber();
if (SysUtil.isDiskSpaceBelowCriticalLevel(requiredSize, driveId))
{
Log.logWarning("Drive " + FileUtils.getDriveName(driveId) +
" (" + driveId + ") does not have enough " +
" free space, required space " + requiredSize +
" bytes");
aDrives.removeElementAt(i);
i--; // Decrease index because drive was removed from Vector.
}
}
if (aDrives.size() == 0)
{
// None of the available drives has enough space,
// throw an exception.
throw InstallerException.getOutOfDiskSpaceException(
requiredSize, null);
}
}
}