javamanager/javainstaller/installer/javasrc/com/nokia/mj/impl/installer/midp2/install/steps/ConfirmInstallation.java
author hgs
Mon, 04 Oct 2010 11:29:25 +0300
changeset 78 71ad690e91f5
parent 67 63b81d807542
permissions -rw-r--r--
v2.2.17_1

/*
* 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.
        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);

        // 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);
        }
    }
}