javamanager/javainstaller/installer/javasrc/com/nokia/mj/impl/installer/GetComponentInfo.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 14 Sep 2010 21:06:50 +0300
branchRCL_3
changeset 71 d5e927d5853b
parent 66 2455ef1f5bbc
child 83 26b2b12093af
permissions -rw-r--r--
Revision: v2.2.11 Kit: 201035

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

import com.nokia.mj.impl.installer.applicationregistrator.SifRegistrator;
import com.nokia.mj.impl.installer.storagehandler.ApplicationInfo;
import com.nokia.mj.impl.installer.storagehandler.SuiteInfo;
import com.nokia.mj.impl.installer.storagehandler.StorageHandler;
import com.nokia.mj.impl.installer.integrityservice.IntegrityService;
import com.nokia.mj.impl.installer.jadjarmatcher.JadJarFile;
import com.nokia.mj.impl.installer.jadjarmatcher.JadJarMatcher;
import com.nokia.mj.impl.installer.jadjarmatcher.MidletMessageHandler;
import com.nokia.mj.impl.installer.utils.Args;
import com.nokia.mj.impl.installer.utils.ComponentId;
import com.nokia.mj.impl.installer.utils.FileUtils;
import com.nokia.mj.impl.installer.utils.InstallerException;
import com.nokia.mj.impl.installer.utils.JadReader;
import com.nokia.mj.impl.installer.utils.Log;
import com.nokia.mj.impl.installer.utils.MidpAttributeValidator;
import com.nokia.mj.impl.installer.utils.PlatformUid;
import com.nokia.mj.impl.security.midp.authentication.AuthenticationModule;
import com.nokia.mj.impl.security.midp.common.AuthenticationCredentials;
import com.nokia.mj.impl.security.midp.common.SecurityAttributes;
import com.nokia.mj.impl.utils.Attribute;
import com.nokia.mj.impl.utils.JarManifestReader;
import com.nokia.mj.impl.utils.Tokenizer;
import com.nokia.mj.impl.utils.Uid;
import com.nokia.mj.impl.utils.Version;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;

/**
 * This class implements functionlity for getting information of
 * an installed or not installed application.
 */
public class GetComponentInfo
{
    // Constants for install status.
    static final int NEW_COMPONENT = 0;
    static final int UPGRADE = 1;
    static final int ALREADY_INSTALLED = 2;
    static final int NEWER_VERSION_ALREADY_INSTALLED = 3;
    static final int INVALID_PACKAGE = 4;

    // There can be only one instance of IntegrityService at a time
    // so concurrent execution of getComponentInfo must be limited.
    private static final Object iSync = new Object();
    private IntegrityService iIntegrityService = null;

    // Command line arguments
    private Args iArgs = null;
    private String iJarFilename = null;
    private String iJadFilename = null;
    private String iJadCharset = null;
    private Uid iUid = null;
    private int[] iCommsResultEndpoints = null;
    private int iTmpDrive = -1;

    // Jad/Manifest attributes
    private Hashtable iJarAttributes = null;
    private Hashtable iJadAttributes = null;
    private Hashtable iCombinedAttributes = null;

    // Suite info from JavaStorage
    private SuiteInfo iSuite = null;

    // Security information
    private SecurityAttributes iSecurityAttributes = null;
    private boolean iIsTrusted = false;

    // Install status.
    private int iInstallStatus = NEW_COMPONENT;
    private Exception iException = null;

    /**
     * Default constructor.
     */
    public GetComponentInfo()
    {
    }

    /**
     * Main method for getting component info. This method sends
     * information of the requested component using InstallerResultMessage
     * to the Comms endpoint given as an argument. Component can be
     * identifed using either -jad, -jar or -uid argument.
     */
    public int getComponentInfo(Args aArgs)
    {
        int result = Installer.ERR_NONE;
        synchronized (iSync)
        {
            Log.log("getComponentInfo begins");

            // Reset member variables.
            iJarAttributes = null;
            iJadAttributes = null;
            iCombinedAttributes = null;
            iSuite = null;

            // Get IntegrityService instance.
            iIntegrityService = IntegrityService.getInstance(
                                    FileUtils.getIntegrityServiceRoot());

            // Get component info.
            try
            {
                result = parseArgs(aArgs);
                if (result == Installer.ERR_NONE)
                {
                    findJadJar();
                    getAttributes();
                    result = getFromStorage();
                }
                if (result == Installer.ERR_NONE)
                {
                    authenticate();
                    getFromSif();
                }
            }
            catch (Exception e)
            {
                result = Installer.ERR_GENERAL;
                iException = e;
                Log.logError("Getting component info failed", e);
            }

            // Close IntegrityService by making rollback.
            if (iIntegrityService.rollback())
            {
                Log.log("IntegrityService rolled back");
            }
            else
            {
                Log.logError("IntegrityService rollback failed");
            }

            // Send InstallerResultMessage.
            try
            {
                sendResult(result);
            }
            catch (Throwable t)
            {
                Log.logError("Sending component info failed", t);
                result = Installer.ERR_GENERAL;
            }
            Log.log("getComponentInfo returns " + result);
        }
        return result;
    }

    /**
     * Parses command line arguments.
     */
    private int parseArgs(Args aArgs)
    {
        Log.log("parseArgs begins");
        int result = Installer.ERR_NONE;
        iArgs = aArgs;
        iJadFilename = iArgs.get("jad");
        iJarFilename = iArgs.get("jar");
        if (iJadFilename != null && iJarFilename != null)
        {
            InstallerException.internalError(
                "Specify either jad or jar as an argument, not both.");
        }
        iJadCharset = iArgs.get("charset");

        String arg = iArgs.get("uid");
        if (arg != null)
        {
            if (iJadFilename != null || iJarFilename != null)
            {
                InstallerException.internalError(
                    "Specify either jad, jar or uid as an argument.");
            }
            Uid uid = PlatformUid.createUid(arg);
            if (uid == null)
            {
                InstallerException.internalError("Invalid uid " + arg);
            }
            iUid = uid;
        }

        arg = iArgs.get("commsresult");
        if (arg != null)
        {
            iCommsResultEndpoints = InstallerResultMessage.parseEndpoints(arg);
        }

        // Check that file exists and store temporary files to the
        // same drive where the component is located.
        if (iJadFilename != null)
        {
            iTmpDrive = FileUtils.getDrive(iJadFilename);
            if (!FileUtils.exists(iJadFilename) &&
                    !FileUtils.isInboxFile(iJadFilename))
            {
                result = Installer.ERR_NOT_FOUND;
                Log.log("File not found: " + iJadFilename);
            }
        }
        if (iJarFilename != null)
        {
            iTmpDrive = FileUtils.getDrive(iJarFilename);
            if (!FileUtils.exists(iJarFilename) &&
                    !FileUtils.isInboxFile(iJarFilename))
            {
                result = Installer.ERR_NOT_FOUND;
                Log.log("File not found: " + iJarFilename);
            }
        }
        Log.log("parseArgs returns " + result);
        return result;
    }

    /**
     * Finds matching Jad and Jar.
     */
    private void findJadJar()
    {
        Log.log("findJadJar begins");

        // Initialise JadJarMatcher classes.
        JadJarMatcher.setIntegrityService(iIntegrityService);
        MidletMessageHandler.setInstallationDrive(iTmpDrive);

        // Try to find jar basing on local jad file.
        if (iJadFilename != null)
        {
            JadJarFile jadJarFile =
                JadJarMatcher.findJar(iJadFilename, iJadCharset);
            iJadFilename = jadJarFile.iJadFilename;
            iJarFilename = jadJarFile.iJarFilename;
            iJadAttributes = jadJarFile.iJadAttributes;
            iJarAttributes = jadJarFile.iJarAttributes;
            if (iJarFilename != null)
            {
                Log.log("Found matching Jar: " + iJarFilename);
            }
            else
            {
                Log.log("Matching Jar not found locally");
            }
        }
        // Try to find jad basing on local jar file.
        else if (iJarFilename != null)
        {
            JadJarFile jadJarFile =
                JadJarMatcher.findJad(iJarFilename);
            iJadFilename = jadJarFile.iJadFilename;
            iJarFilename = jadJarFile.iJarFilename;
            iJadAttributes = jadJarFile.iJadAttributes;
            iJarAttributes = jadJarFile.iJarAttributes;
            if (iJadFilename != null)
            {
                Log.log("Found matching Jad: " + iJadFilename);
            }
            else
            {
                Log.log("Matching Jad not found locally");
            }
        }

        Log.log("findJadJar returns");
    }

    /**
     * Gets and validates jad and manifest attributes.
     */
    private void getAttributes() throws IOException
    {
        Log.log("getAttributes begins");
        if (iJadFilename != null && iJadAttributes == null)
        {
            Log.log("Reading Jad...");
            iJadAttributes =
                JadReader.getAttributes(iJadFilename, iJadCharset);
            if (iJadAttributes == null)
            {
                throw new IOException(
                    "No Jad attributes found from " + iJadFilename);
            }
        }

        if (iJarFilename != null && iJarAttributes == null)
        {
            Log.log("Reading Jar...");
            iJarAttributes =
                JarManifestReader.getAttributes(iJarFilename);
            if (iJarAttributes == null)
            {
                throw new IOException(
                    "No Jar attributes found from " + iJarFilename);
            }
        }

        try
        {
            // Validate and combine the attributes.
            MidpAttributeValidator attributeValidator =
                new MidpAttributeValidator();
            if (iJadAttributes != null)
            {
                attributeValidator.validate(
                    iJadAttributes, attributeValidator.JAD);
                // Get security attributes.
                iSecurityAttributes = new SecurityAttributes();
                iSecurityAttributes.addDescriptorAttributes(iJadAttributes);
                iIsTrusted = iSecurityAttributes.isTrusted();
            }
            if (iJarAttributes != null)
            {
                attributeValidator.validate(
                    iJarAttributes, attributeValidator.JAR);
            }
            iCombinedAttributes = attributeValidator.combine(
                                      iJadAttributes, iJarAttributes, iIsTrusted);
        }
        catch (Exception e)
        {
            // Attribute validation failure means that
            // application cannot be installed.
            iInstallStatus = INVALID_PACKAGE;
            iException = e;
            Log.log("Attribute validation failed", e);
        }
        Log.log("getAttributes returns");
    }

    /**
     * Gets application suite information from JavaStorage.
     */
    private int getFromStorage()
    {
        Log.log("getFromStorage begins");
        int result = Installer.ERR_NONE;
        StorageHandler storageHandler = null;
        try
        {
            storageHandler = new StorageHandler();
            if (iUid != null)
            {
                // Read from JavaStorage by uid.
                Uid uid = storageHandler.getSuiteUid(iUid);
                if (uid != null)
                {
                    iSuite = new SuiteInfo(uid);
                }
            }
            else
            {
                // Read from JavaStorage by name and vendor.
                iSuite = new SuiteInfo(
                    getAttributeValue("MIDlet-Name"),
                    getAttributeValue("MIDlet-Vendor"));
            }
            if (iSuite != null && storageHandler.readSuiteInfo(iSuite))
            {
                Log.log("Suite found from JavaStorage: " + iSuite.toString());
                iInstallStatus = ALREADY_INSTALLED;
                Version version = Version.getVersion(
                                      getAttributeValue("MIDlet-Version"));
                if (version != null)
                {
                    int versionComparison =
                        version.compareTo(iSuite.getVersion());
                    if (versionComparison > 0)
                    {
                        iInstallStatus = UPGRADE;
                    }
                    else if (versionComparison == 0)
                    {
                        iInstallStatus = ALREADY_INSTALLED;
                    }
                    else if (versionComparison < 0)
                    {
                        iInstallStatus = NEWER_VERSION_ALREADY_INSTALLED;
                    }
                }
            }
            else
            {
                Log.log("Suite not found from JavaStorage");
                iSuite = null;
                if (iUid != null)
                {
                    // If component info is requested by uid
                    // and not found from JavaStorage, return
                    // "not found" error code.
                    result = Installer.ERR_NOT_FOUND;
                }
            }
        }
        finally
        {
            if (storageHandler != null)
            {
                storageHandler.close();
            }
        }
        Log.log("getFromStorage returns " + result);
        return result;
    }

    /**
     * Gets component ids from SIF if application is already installed.
     */
    private void getFromSif()
    {
        if (iSuite != null)
        {
            Log.log("getFromSif begins");
            // Suite exists, now get component ids from SIF.
            SifRegistrator sr = new SifRegistrator();
            sr.startSession(false);
            try
            {
                iSuite.setComponentId(sr.getComponentId(iSuite.getGlobalId()));
                Log.log("getFromSif " + iSuite.getGlobalId() +
                        ", cid: " + iSuite.getComponentId());
                Vector apps = iSuite.getApplications();
                for (int i = 0; i < apps.size(); i++)
                {
                    ApplicationInfo appInfo =
                        (ApplicationInfo)apps.elementAt(i);
                    appInfo.setComponentId(
                        sr.getComponentId(iSuite.getGlobalId(i)));
                    Log.log("getFromSif " + iSuite.getGlobalId(i) +
                            ", cid: " + appInfo.getComponentId());
                }
            }
            finally
            {
                sr.closeSession();
            }
            Log.log("getFromSif returns");
        }
    }

    /**
     * Authenticates Jad and Jar.
     */
    private void authenticate()
    {
        Log.log("authenticate begins");
        if (iSecurityAttributes != null)
        {
            try
            {
                // Use dummy uid in case suite has not been installed yet.
                Uid suiteUid = PlatformUid.createUid(0);
                if (iSuite != null)
                {
                    suiteUid = iSuite.getUid();
                }

                // Authenticate jad.
                AuthenticationCredentials[] authenticationCredentials =
                    AuthenticationModule.getInstance().authenticateJad(
                        suiteUid, null,
                        iSecurityAttributes.getAuthenticationAttributes());

                if (authenticationCredentials != null)
                {
                    for (int i = 0; i < authenticationCredentials.length; i++)
                    {
                        String domain = authenticationCredentials[i]
                            .getProtectionDomainCategory();
                        Log.log("Protection domain: " + domain);
                    }
                }
                if (iJarFilename != null)
                {
                    // Authenticate jar.
                    AuthenticationModule.getInstance().authenticateJar(
                        suiteUid, null, iJarFilename,
                        FileUtils.isDrmProtected(iJarFilename));
                }
            }
            catch (Exception e)
            {
                // If authentication fails it means that
                // application cannot be installed.
                iInstallStatus = INVALID_PACKAGE;
                iException = e;
                Log.log("Authentication failed", e);
            }
        }
        Log.log("authenticate returns");
    }

    /**
     * Sends InstallerResultMessage.
     */
    private void sendResult(int aResult)
    {
        if (iCommsResultEndpoints == null ||
                iCommsResultEndpoints.length == 0)
        {
            return;
        }
        InstallerResultMessage msg = new InstallerResultMessage();
        msg.addValue(msg.NAME_OPERATION, 2);
        msg.addResult(iException);
        msg.addValue(msg.NAME_RESULT, aResult);
        if (aResult == Installer.ERR_NONE)
        {
            // Init msg either using iSuite, iCombinedAttributes or both.
            if (iSuite != null && iCombinedAttributes == null)
            {
                // Init msg from iSuite only.
                msg.init(iSuite);
            }
            else if (iCombinedAttributes != null)
            {
                String[][] midletNAttributes = getMidletNAttributeValues();
                if (iSuite != null)
                {
                    // Init available suite and application uids from iSuite.
                    msg.addValue(msg.NAME_SUITE_UID,
                                 PlatformUid.getIntValue(iSuite.getUid()));
                    if (iSuite.getComponentId() != null)
                    {
                        msg.addValue(msg.NAME_SUITE_CID,
                                     iSuite.getComponentId().getId());
                    }
                    Vector apps = iSuite.getApplications();
                    for (int i = 0; i < midletNAttributes.length; i++)
                    {
                        int index = i+1;
                        if (i < apps.size())
                        {
                            ApplicationInfo app = (ApplicationInfo)apps.elementAt(i);
                            msg.addValue(msg.NAME_MIDLET_UID+index,
                                         PlatformUid.getIntValue(app.getUid()));
                            if (app.getComponentId() != null)
                            {
                                msg.addValue(msg.NAME_MIDLET_CID+index,
                                             app.getComponentId().getId());
                            }
                        }
                    }
                }

                // Init message from attributes read from jad/jar.
                // Note that uids for not installed applications
                // are not returned, unless they are defined with
                // Nokia-MIDlet-UID-n attribute.
                msg.addValue(msg.NAME_SUITE_GID,
                             SuiteInfo.getGlobalId(
                                 getAttributeValue("MIDlet-Vendor"),
                                 getAttributeValue("MIDlet-Name"), null));
                addAttributeValue(msg, msg.NAME_SUITE_NAME, "MIDlet-Name");
                addAttributeValue(msg, msg.NAME_VENDOR, "MIDlet-Vendor");
                addAttributeValue(msg, msg.NAME_VERSION, "MIDlet-Version");
                for (int i = 0; i < midletNAttributes.length; i++)
                {
                    int index = i+1;
                    String midletName = midletNAttributes[i][0].trim();
                    msg.addValue(msg.NAME_MIDLET_NAME+index, midletName);
                    msg.addValue(msg.NAME_MIDLET_GID+index,
                                 SuiteInfo.getGlobalId(
                                     getAttributeValue("MIDlet-Vendor"),
                                     getAttributeValue("MIDlet-Name"),
                                     midletName));
                    String midletUid =
                        getAttributeValue("Nokia-MIDlet-UID-" + index);
                    if (midletUid != null)
                    {
                        msg.addValue(
                            msg.NAME_MIDLET_UID+index,
                            PlatformUid.getIntValue(
                                PlatformUid.createUid(midletUid)));
                    }
                }

                // Calculate size of the installation package.
                int initialSize = 0;
                String dataSize = getAttributeValue("MIDlet-Data-Size");
                String jarSize = getAttributeValue("MIDlet-Jar-Size");
                try
                {
                    if (dataSize != null)
                    {
                        initialSize += Integer.parseInt(dataSize);
                    }
                    if (jarSize != null)
                    {
                        initialSize += Integer.parseInt(jarSize);
                    }
                }
                catch (NumberFormatException nfe)
                {
                    // ignore
                }
                if (initialSize == 0 && iJarFilename != null)
                {
                    // Get initialSize from jar file size.
                    initialSize = (int)FileUtils.getSize(iJarFilename);
                }
                msg.addValue(msg.NAME_COMPONENT_SIZE, initialSize);
            }
            msg.addValue(msg.NAME_INSTALL_STATUS, iInstallStatus);
            if (iInstallStatus != INVALID_PACKAGE)
            {
                // Add authenticity value only when the package is found
                // to be valid.
                msg.addValue(msg.NAME_AUTHENTICITY, (iIsTrusted? 1: 0));
            }
        }
        Log.log("Sending " + msg.toString());
        msg.send(iCommsResultEndpoints);
    }

    /**
     * Adds specified value to given message if the value exists
     * in iCombinedAttributes member. If value does not exist in
     * iCombinedAttributes then value is removed from the message.
     */
    private void addAttributeValue(
        InstallerResultMessage aMsg, String aValueName, String aAttrName)
    {
        String value = getAttributeValue(aAttrName);
        if (value == null)
        {
            aMsg.removeValue(aValueName);
        }
        else
        {
            aMsg.addValue(aValueName, value);
        }
    }

    /**
     * Returns an array of MIDlet-n attribute values from iCombinedAttaributes.
     */
    private String[][] getMidletNAttributeValues()
    {
        String attrName = "MIDlet-";
        String attrValue = "";
        Vector attrs = new Vector();
        int i = 1;
        while (attrValue != null)
        {
            attrValue = getAttributeValue(attrName + i);
            if (attrValue != null)
            {
                String[] attrTokens = Tokenizer.split(attrValue, ",");
                // attrTokens has midlet name, icon, and class.
                attrs.addElement(attrTokens);
            }
            i++;
        }
        String[][] result = new String[attrs.size()][];
        attrs.copyInto(result);
        return result;
    }

    /**
     * Returns a string type attribute value from iCombinedAttributes.
     */
    private String getAttributeValue(String aName)
    {
        if (iCombinedAttributes == null)
        {
            return null;
        }
        Attribute attr = (Attribute)iCombinedAttributes.get(aName);
        if (attr != null)
        {
            return attr.getValue();
        }
        return null;
    }
}