--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javamanager/preinstaller/src.s60/silentmidletinstall.cpp Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1167 @@
+/*
+* Copyright (c) 2008 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: OMJ S60 preinstaller process
+*
+*/
+
+
+#include <e32base.h>
+#include <pathinfo.h>
+#include <driveinfo.h>
+#include <javastorageentry.h>
+#include <javastorage.h>
+#include <javastoragenames.h>
+#include <utf.h>
+
+#include "silentmidletinstall.h"
+#include "javacommonutils.h"
+#include "logger.h"
+#include "javaprocessconstants.h"
+#include "javasymbianoslayer.h"
+
+using namespace java::storage;
+using namespace java::util;
+
+
+// MIDlet name and vendor max size is 255,
+// +1 added for terminating zero needed for logging
+const TInt KMaxBufferSize = 256;
+
+// character constants needed when parsing java attributes
+const TUint32 HT = 9; // horizontal tab
+const TUint32 LF = 10; // line feed
+const TUint32 CR = 13; // carriage return
+const TUint32 SP = 32; // space
+const TUint32 COLON = 58; // ':'
+
+const int INSTALLED = 0;
+const int NO_PREINSTALL = 2;
+
+_LIT(KMidletName, "MIDlet-Name");
+_LIT(KMidletVendor, "MIDlet-Vendor");
+_LIT(KMidletVersion, "MIDlet-Version");
+
+_LIT(KJadExtension, "*.jad");
+
+// Java Installer Integrity Service journal file name
+_LIT(KISJournalFile, "\\private\\102033E6\\installer\\is\\isjournal.dat");
+
+// The directory where the java applications to be preinstalled are searched
+// from
+_LIT(KPreinstallDir, ":\\resource\\java\\preinstall\\");
+
+
+/**
+ * To create new instance of this class.
+ *
+ * @param aFs - A reference to the file server.
+ * @return Reference to the object of this class.
+ * @exception If construction fails.
+ */
+CSilentMIDletInstall* CSilentMIDletInstall::NewLC(RFs& aFs)
+{
+ CSilentMIDletInstall* self = new(ELeave) CSilentMIDletInstall(aFs);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+}
+
+/**
+ * To do 1st phase construction for this object.
+ * Adds this active object to the scheduler.
+ *
+ * @param aFs - A reference to the file server.
+ */
+CSilentMIDletInstall::CSilentMIDletInstall(RFs& aFs) :
+ CActive(EPriorityStandard), iFs(aFs)
+{
+ CActiveScheduler::Add(this);
+}
+
+/**
+ * To do 2nd phase construction for this object.
+ *
+ * @exception If the method is not able to allocate necessary buffers.
+ */
+void CSilentMIDletInstall::ConstructL()
+{
+ JELOG2(EJavaPreinstaller);
+ iMIDletName = HBufC::NewL(KMaxBufferSize);
+ iMIDletVendor = HBufC::NewL(KMaxBufferSize);
+ iPreinstallServer = new(ELeave) PreinstallCommsServer();
+
+ iNumberOfAppsToInstall = 0;
+ iISRollbackNeeded = EFalse;
+}
+
+/**
+ * Deletes this object.
+ * All allocated resources are released.
+ */
+CSilentMIDletInstall::~CSilentMIDletInstall()
+{
+ JELOG2(EJavaPreinstaller);
+ iJadFiles.ResetAndDestroy();
+ iJadFiles.Close();
+
+ iDirs.ResetAndDestroy();
+ iDirs.Close();
+
+ delete iMIDletName;
+ iMIDletName = NULL;
+
+ delete iMIDletVendor;
+ iMIDletVendor = NULL;
+
+ delete iPreinstallServer;
+ iPreinstallServer = NULL;
+}
+
+/**
+ * To start silent preinstallations.
+ */
+void CSilentMIDletInstall::Start()
+{
+ JELOG2(EJavaPreinstaller);
+ iState = EFindOutDeviceDrives;
+
+ // Check if an explicit roll-back of a previous installation is needed
+ // in the case there is nothing to pre-install (if there is something
+ // to pre-install, the potential roll-back is done automatically)
+ TUint attrs;
+ TInt err = iFs.Att(KISJournalFile, attrs);
+ LOG1(EJavaPreinstaller, EInfo,
+ "Checking IS journal file \\private\\102033E6\\installer\\is\\isjournal.dat, status %d", err);
+ if ((KErrNotFound == err) || (KErrPathNotFound == err))
+ {
+ iISRollbackNeeded = EFalse;
+ }
+ else
+ {
+ iISRollbackNeeded = ETrue;
+ // If JavaInstaller is running, then existence of the Java Installer
+ // integrity service directory is ok and rollback is not needed
+ TFullName processName;
+ _LIT(KJavaInstallerProcess, "*Installer*");
+ TFindProcess finder(KJavaInstallerProcess);
+ err = finder.Next(processName);
+ if (err == KErrNone)
+ {
+ iISRollbackNeeded = EFalse;
+ WLOG(EJavaPreinstaller,
+ "Java Installer is running while checking need to rollback");
+ }
+ }
+
+ CompleteRequest();
+}
+
+/**
+ * To stop whole preinstaller.
+ * Stops the active scheduler.
+ */
+void CSilentMIDletInstall::Exit()
+{
+ CActiveScheduler::Stop();
+}
+
+/**
+ * Completes the current request for this object and
+ * sets this active object to active state.
+ *
+ * @Postconditions The following conditions are true immediately after
+ * returning from this method.
+ * - iStatus == KErrNone
+ * - IsActive() == ETrue
+ */
+void CSilentMIDletInstall::CompleteRequest()
+{
+ JELOG2(EJavaPreinstaller);
+
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ if (!IsActive())
+ {
+ SetActive();
+ }
+}
+
+/**
+ * To run this active object.
+ *
+ * This object goes through the following states in the order the states
+ * are listed below:
+ *
+ * EFindOutDeviceDrives: find out all non-remote drives in the device
+ * - mark externally mountable drives (removable drives)
+ * - mark read only drives (ROM)
+ *
+ * EFindOutDrivesToBeScannedNow: make list of drives to be scanned
+ * - start with all mounted drives
+ *
+ * EAppsInPreinstallDirectories: go through all drives in the list and
+ * find all java applications in the preinstall directory
+ * - only the .jad file is checked but remember that downloading .jar is not
+ * allowed when preinstalling so the .jar file referred to in the .jad
+ * file must be in the device
+ *
+ * ECheckWhichAppsShouldBeInstalled: for each java application check from
+ * Java Storage whether it should be installed
+ * - if same or newer version already installed, do not install
+ * - if the user has uninstalled same or newer version, do not install
+ * - unsigned java applications can be installed only in debug builds
+ *
+ * EExecutePreinstallServer:
+ * - store list of .jad files to preinstall server object,
+ * - start Java Installer in poll mode,
+ * - start preinstall server in another thread and wait until Java Installer exits
+ * - preinstall server will give the names of the .jad files to
+ * Java Installer when it polls them,
+ * - preinstall server will log whether each installation succeeded.
+ * - After all .jad files have been polled, preinstall server will ask
+ * Java Installer to exit.
+ * - when Java Installer exits, this active object moves to next state.
+ *
+ * EExit:
+ * - stop preinstall server thread
+ * - notify possibly waiting processes that preinstallation has been done
+ * - exit
+ *
+ */
+void CSilentMIDletInstall::RunL()
+{
+ JELOG2(EJavaPreinstaller);
+
+ switch (iState)
+ {
+ case EFindOutDeviceDrives:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL EFindOutDeviceDrives");
+ GetAllDeviceDrivesL();
+ iState = EFindOutDirectoriesToBeScannedNow;
+ CompleteRequest();
+ }
+ break;
+
+ case EFindOutDirectoriesToBeScannedNow:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL EFindOutDirectoriesToBeScannedNow");
+
+ GetDirsToBeScannedL();
+
+ iState = EAppsInPreinstallDirectories;
+ CompleteRequest();
+ }
+ break;
+
+ case EAppsInPreinstallDirectories:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL EAppsInPreinstallDirectories");
+
+ GetMIDletFilesL(iDirs);
+
+ iState = ECheckWhichAppsShouldBeInstalled;
+ CompleteRequest();
+ }
+ break;
+
+ case ECheckWhichAppsShouldBeInstalled:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL ECheckWhichAppsShouldBeInstalled");
+
+ CheckWhichAppsShouldBeInstalledL();
+
+ iState = EExecutePreinstallServer;
+ CompleteRequest();
+ }
+ break;
+
+ case EExecutePreinstallServer:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL EExecutePreinstallServer");
+
+ // This method advances the state machine to EExit state
+ ExecutePreinstallServerL();
+ }
+ break;
+
+ case EExit:
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall:RunL EExit");
+
+ // Stop the server if it is running
+ iPreinstallServer->stop();
+
+ // Java Captain starts preinstaller each time a mountable drive
+ // is added to the device. There is no need to wait for mount
+ // events in this process.
+
+ // Stop the whole preinstaller process.
+ Exit();
+ }
+ break;
+
+ } // switch
+}
+
+/**
+ * To handle leave from RunL.
+ * This method re-activates this active object.
+ * After calling this method this active object is in stable state and
+ * ready to continue its execution
+ *
+ * @param aError - A reason of error.
+ * @return KErrNone.
+ */
+TInt CSilentMIDletInstall::RunError(TInt aError)
+{
+ ELOG2(EJavaPreinstaller,
+ "CSilentMIDletInstall::RunError(%d) from state %d", aError, iState);
+
+ Cancel();
+
+ // Make preinstaller to exit nicely.
+ iState = EExit;
+ CompleteRequest();
+
+ return KErrNone;
+}
+
+/**
+ * Cancel this object. Stops preinstall server if needed.
+ */
+void CSilentMIDletInstall::DoCancel()
+{
+ ELOG1(EJavaPreinstaller,
+ "CSilentMIDletInstall::DoCancel from state %d", iState);
+
+ // Check whether preinstall server must be stopped
+ if (iState == EExecutePreinstallServer)
+ {
+ // Stops the server if it is running
+ iPreinstallServer->stop();
+ }
+}
+
+/**
+ * Add .jad files in the directories specified in aDirs to iJadFiles.
+ *
+ * Goes all directories in iDirs one by one and adds the
+ * valid .jad files in alphabetical order to iJadFiles.
+ *
+ * @param aDirs - An array of directories to be scanned.
+ * @exception Unable to alloc memory for the internal buffers.
+ * @exception Unable to get directory's contents.
+ */
+void CSilentMIDletInstall::GetMIDletFilesL(RPointerArray<HBufC>& aDirs)
+{
+ JELOG2(EJavaPreinstaller);
+
+ const TInt num = aDirs.Count();
+
+ // Read JAD files.
+ for (TInt i = 0; i < num; i++)
+ {
+ const TPtrC& dir = ((HBufC *)(aDirs[i]))->Des();
+
+ HBufC* mask= NULL;
+ TPtr maskPtr(NULL,0,0);
+
+ // Create JAD mask, e.g., "z:\resource\java\preinstall\*.jad".
+ // Reserve space also for terminating zero for logging the info
+ mask = HBufC::NewLC(dir.Length() + KJadExtension().Length() + 1);
+ maskPtr.Set(mask->Des());
+ maskPtr.Append(dir);
+ maskPtr.Append(KJadExtension);
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::GetMIDletFilesL Looking for jad files from directory %s",
+ (wchar_t *)(maskPtr.PtrZ()));
+ GetDirEntriesL(dir, *mask, iJadFiles);
+ CleanupStack::PopAndDestroy(mask);
+ }
+
+ if (iJadFiles.Count() == 0)
+ {
+ ILOG(EJavaPreinstaller,
+ "CSilentMIDletInstall:GetMIDletFilesL No MIDlets to preinstall");
+ }
+}
+
+/**
+ * Get the entries of the directory in alphabetical order.
+ *
+ * @param aDirectory - A directory to be scanned.
+ * @param aMask - A filter to be used for scanning.
+ * @param aVector - An output vector for contents.
+ * @exception Unable to alloc memory for the internal buffers.
+ */
+void CSilentMIDletInstall::GetDirEntriesL(const TDesC& aDirectory,
+ const TDesC& aMask, RPointerArray<HBufC>& aVector)
+{
+ JELOG2(EJavaPreinstaller);
+
+ // Get entries in alphabetical order.
+
+ CDir* entries= NULL;
+ TInt err = iFs.GetDir(aMask, KEntryAttMaskSupported, ESortByName, entries);
+
+ if (err != KErrNone)
+ {
+ if (err != KErrPathNotFound)
+ {
+ ELOG1(EJavaPreinstaller,
+ "CSilentMIDletInstall::GetDirEntriesL Dir error (%d)", err);
+ }
+ delete entries;
+ return;
+ }
+
+ CleanupStack::PushL(entries);
+
+ // Add full file names to the vector.
+
+ TInt num = entries->Count();
+ for (TInt i = 0; i < num; i++)
+ {
+ const TDesC& name = (*entries)[i].iName;
+ // Reserve one char for null terminator
+ HBufC* path = HBufC::NewLC(aDirectory.Length() + name.Length() + 1);
+ TPtr pathPtr(path->Des());
+ pathPtr.Append(aDirectory);
+ pathPtr.Append(name);
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::GetDirEntriesL Adding file %s",
+ (wchar_t *)(pathPtr.PtrZ()));
+ aVector.Append(path);
+ CleanupStack::Pop(path);
+ }
+
+ CleanupStack::PopAndDestroy(entries);
+}
+
+/**
+ * Start Java Installer in poll mode and then wait until it exits.
+ */
+void CSilentMIDletInstall::RunJavaInstallerL()
+{
+ JELOG2(EJavaPreinstaller);
+
+ LOG(EJavaPreinstaller, EInfo, "CSilentMIDletInstall::RunJavaInstaller");
+
+ RProcess rJavaInstaller;
+ TFileName fileName;
+ // Max one path name, user name and password and some options ->
+ // 1536 is enough
+ TBuf<1536> commandLine;
+
+ // Build command line used to pass all necessary info to Java Installer
+ TInt len = strlen(java::runtime::JAVA_INSTALLER_STARTER_DLL);
+ TPtr8 ptr8InstallerDll((TUint8 *)java::runtime::JAVA_INSTALLER_STARTER_DLL, len, len);
+ commandLine.Copy(ptr8InstallerDll);
+
+ commandLine.Append(_L(" poll -address=preinstall"));
+
+ // Upgrading MIDlets is allowed
+ commandLine.Append(_L(" -upgrade=yes"));
+
+ // No OCSP checks for preinstalled MIDlets
+ commandLine.Append(_L(" -ocsp=no"));
+
+#ifdef _DEBUG
+ // Allow installing unsigned apps in debug builds
+ commandLine.Append(_L(" -untrusted=yes"));
+#else
+ // Deny preinstalling untrusted midlets in release builds
+ commandLine.Append(_L(" -untrusted=no"));
+#endif
+
+ // Do upgrade if version number has not increased
+ commandLine.Append(_L(" -overwrite=no"));
+
+ // Both the JAD and JAR file must be present when preinstalling MIDlets,
+ // downloading is not allowed.
+ commandLine.Append(_L(" -download=no"));
+
+ // If upgrade install, retain the data (RMS and private files) of the previous version
+ commandLine.Append(_L(" -upgrade_data=yes"));
+
+ // start JavaInstaller
+ TBuf<64> installerProcess; // Actual len of the process name is 9
+ len = strlen(java::runtime::JAVA_PROCESS);
+ TPtr8 ptr8Process((TUint8 *)java::runtime::JAVA_PROCESS, len, len);
+ installerProcess.Copy(ptr8Process);
+
+ TInt err = rJavaInstaller.Create(installerProcess, commandLine);
+ if (KErrNone == err)
+ {
+ LOG(EJavaPreinstaller, EInfo, "CSilentMIDletInstall::RunJavaInstaller calling Logon");
+ // This call will wait until Java Installer exits (or panics)
+ rJavaInstaller.Logon(iStatus);
+
+ LOG(EJavaPreinstaller, EInfo, "CSilentMIDletInstall::RunJavaInstaller calling Resume");
+ rJavaInstaller.Resume();
+ }
+ else
+ {
+ ELOG1(EJavaPreinstaller,
+ "CSilentMIDletInstall::RunJavaInstaller Cannot start Installer, error %d", err);
+ // CActive will trap the following leave, execution will go to RunError
+ User::Leave(err);
+ }
+
+ LOG(EJavaPreinstaller, EInfo, "CSilentMIDletInstall::RunJavaInstaller calling RProcess::Close");
+ // free resources before returning
+ rJavaInstaller.Close();
+
+ // now wait until Java Installer exits
+ SetActive();
+}
+
+/**
+ * Start Java Installer just to do IntegrityService rollback.
+ * Do not wait for the process exit.
+ */
+void CSilentMIDletInstall::RollbackJavaInstaller()
+{
+ JELOG2(EJavaPreinstaller);
+
+ RProcess rJavaInstaller;
+ TFileName fileName;
+ // Pass just 'rollback' command
+ TBuf<128> commandLine;
+
+ // Build command line used to pass all necessary info to Java Installer
+ TInt len = strlen(java::runtime::JAVA_INSTALLER_STARTER_DLL);
+ TPtr8 ptr8InstallerDll((TUint8 *)java::runtime::JAVA_INSTALLER_STARTER_DLL, len, len);
+ commandLine.Copy(ptr8InstallerDll);
+
+ commandLine.Append(_L(" rollback"));
+
+ WLOG(EJavaPreinstaller,
+ "CSilentMIDletInstall::RollbackJavaInstaller starting Java Installer for rollback");
+
+ // start JavaInstaller
+ TBuf<64> installerProcess; // Actual len of the process name is 9
+ len = strlen(java::runtime::JAVA_PROCESS);
+ TPtr8 ptr8Process((TUint8 *)java::runtime::JAVA_PROCESS, len, len);
+ installerProcess.Copy(ptr8Process);
+
+ TInt err = rJavaInstaller.Create(installerProcess, commandLine);
+ if (KErrNone == err)
+ {
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::RollbackJavaInstaller calling Resume");
+ rJavaInstaller.Resume();
+ }
+
+ LOG(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::RollbackJavaInstaller calling RProcess::Close");
+ // free resources before returning
+ rJavaInstaller.Close();
+}
+
+/**
+ * Parse MIDlet-Name, MIDlet-Vendor and MIDlet-Version parameters from JAD file.
+ * Parameters are used to determine whether to pre-install MIDlet or not.
+ *
+ * @param ETrue if parsing succeeds otherwise EFalse.
+ */
+TBool CSilentMIDletInstall::ParseJadL(const TDesC& aJadFileName)
+{
+ JELOG2(EJavaPreinstaller);
+
+ HBufC *jadContent = NULL;
+ // Trap leave thrown if reading jad content fails
+ TRAPD(err, jadContent = GetJadContentL(aJadFileName));
+ if (KErrNone != err)
+ {
+ ELOG1(EJavaPreinstaller,
+ "CSilentMIDletInstall::ParseJadL Reading Jad content failed, error %d",
+ err);
+ return EFalse;
+ }
+ CleanupStack::PushL(jadContent);
+
+ HBufC *midletName = ParseAttribute(jadContent, KMidletName);
+ if (NULL == midletName)
+ {
+ ELOG(EJavaPreinstaller,
+ "CSilentMIDletInstall::ParseJadL Parsing midlet name failed.");
+ CleanupStack::PopAndDestroy(jadContent);
+ return EFalse;
+ }
+ // store midlet name to member variable and log it
+ TPtr namePtr(iMIDletName->Des());
+ namePtr.Copy(*midletName);
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::ParseJadL MIDlet-Name %s",
+ (wchar_t *)(namePtr.PtrZ()));
+ delete midletName;
+
+ HBufC *midletVendor = ParseAttribute(jadContent, KMidletVendor);
+ if (NULL == midletVendor)
+ {
+ ELOG(EJavaPreinstaller,
+ "CSilentMIDletInstall::ParseJadL Parsing midlet vendor failed.");
+ CleanupStack::PopAndDestroy(jadContent);
+ return EFalse;
+ }
+ // store midlet vendor to member variable and log it
+ TPtr vendorPtr(iMIDletVendor->Des());
+ vendorPtr.Copy(*midletVendor);
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::ParseJadL MIDlet-Vendor %s",
+ (wchar_t *)(vendorPtr.PtrZ()));
+ delete midletVendor;
+
+ HBufC *midletVersion = ParseAttribute(jadContent, KMidletVersion);
+ if (NULL == midletVersion)
+ {
+ ELOG(EJavaPreinstaller,
+ "CSilentMIDletInstall::ParseJadL Parsing midlet version failed.");
+ CleanupStack::PopAndDestroy(jadContent);
+ return EFalse;
+ }
+ // Convert version to TAppVersion, store it and log it
+ iMIDletVersion = DesToAppVersion(midletVersion);
+ delete midletVersion;
+
+ LOG3(EJavaPreinstaller, EInfo,
+ "CSilentMIDletInstall::ParseJadL MIDlet-Version is %d.%d.%d",
+ iMIDletVersion.iMajor, iMIDletVersion.iMinor, iMIDletVersion.iBuild);
+
+ CleanupStack::PopAndDestroy(jadContent);
+ return ETrue;
+}
+
+/**
+ * If there is something to preinstall start preinstall comms server and
+ * installer in poll mode.
+ * Otherwise start installer with rollback option if installer
+ * integrity service rollback is needed.
+ */
+void CSilentMIDletInstall::ExecutePreinstallServerL()
+{
+ if (iNumberOfAppsToInstall > 0)
+ {
+ // Install all MIDlet Suites still in iJadFiles.
+
+ // Pass iJadFiles to PreinstallServer
+ iPreinstallServer->setJadFiles(iJadFiles);
+
+ // Start the comms server
+ int err = iPreinstallServer->start();
+ if (0 != err)
+ {
+ // server cannot be started
+ ELOG1(EJavaPreinstaller,
+ "Cannot start preinstall server, err %d", err);
+ }
+ else
+ {
+ // Starts Java Installer and waits until it exits
+ RunJavaInstallerL();
+ }
+
+ iState = EExit;
+ }
+ else
+ {
+ // Rollback must be done by launching Java Installer
+ // separately with rollback option.
+ // (Normally rollback is done during normal installation.)
+ if (iISRollbackNeeded)
+ {
+ RollbackJavaInstaller();
+ iISRollbackNeeded = EFalse;
+ }
+
+ iState = EExit;
+ CompleteRequest();
+ }
+}
+
+/**
+ * Check all Jad files in iJadFiles and remove those
+ * that need not be preinstalled (already installed[1] or preinstalled[2]
+ * or preinstalled and then uninstalled by user[3]).
+ */
+void CSilentMIDletInstall::CheckWhichAppsShouldBeInstalledL()
+{
+ TBool skipInstall(ETrue);
+ iNumberOfAppsToInstall = 0;
+
+ // throws STL C++ exception if fails, catched by CActive and
+ // execution goes to RunError
+ std::auto_ptr<JavaStorage> js(JavaStorage::createInstance());
+ // throws STL C++ exception if fails, catched by CActive and
+ // execution goes to RunError
+ js->open(JAVA_DATABASE_NAME);
+
+ // In Java Storage there is 'preinstall' table that contains
+ // the name, vendor and version of every java application that is currently
+ // installed to the device or that has been preinstalled to the device
+ // but removed by the user.
+ // The table contains also the installation state of each application.
+ // If the application has been removed the user, the application is in state
+ // NO_PREINSTALL.
+ // Do not preinstall application if it is found from this table with state
+ // NO_PREINSTALL.
+ // Do not preinstall application if it is found from this table
+ // and the version number of the application is the same or less
+ // than the version number in the table.
+
+ JavaStorageEntry attribute;
+ JavaStorageApplicationEntry_t findPattern;
+ JavaStorageApplicationList_t foundEntries;
+
+ for (TInt i = 0; i < iJadFiles.Count(); i++)
+ {
+ if (ParseJadL(*iJadFiles[i]))
+ {
+ skipInstall = ETrue;
+
+ TPtr namePtr(iMIDletName->Des());
+ TPtr vendorPtr(iMIDletVendor->Des());
+ int installState = INSTALLED;
+
+ // Search by NAME and VENDOR
+ attribute.setEntry(NAME, desToWstring(namePtr));
+ findPattern.insert(attribute);
+ attribute.setEntry(VENDOR, desToWstring(vendorPtr));
+ findPattern.insert(attribute);
+
+ // All information in table is returned (whole row)
+
+ js->search(PREINSTALL_TABLE, findPattern, foundEntries);
+ findPattern.clear();
+
+ // Anything found?
+ if (foundEntries.size() > 0)
+ {
+ // We like to know if the application INSTALL_STATE is NO_PREINSTALL
+ attribute.setEntry(INSTALL_STATE, L"");
+
+ // Check the INSTALL_STATE of the first (there should be only one)
+ // found application
+ JavaStorageApplicationEntry_t::const_iterator findIterator =
+ foundEntries.front().find(attribute);
+ if (findIterator != (foundEntries.front()).end())
+ {
+ installState = JavaCommonUtils::wstringToInt(findIterator->entryValue());
+ }
+ else
+ {
+ ELOG1WSTR(EJavaPreinstaller,
+ "CheckWhichAppsShouldBeInstalledL: No INSTALL_STATE info "
+ "in Storage for application %s", desToWstring(namePtr));
+ }
+
+ if (installState == NO_PREINSTALL)
+ {
+ // This application must not be preinstalled
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CheckWhichAppsShouldBeInstalledL: User has removed application %s "
+ "It must not be preinstalled again.", desToWstring(namePtr));
+ }
+ else
+ {
+ // We like to know application VERSION
+ attribute.setEntry(VERSION, L"");
+
+ // Check the version of the first (there should be only one)
+ // found application
+ findIterator = foundEntries.front().find(attribute);
+ if (findIterator != (foundEntries.front()).end())
+ {
+ // Application has been installed at sometime
+ // but if we have a newer version of the application
+ // we will install this newer version.
+ if (iMIDletVersion > wstringToAppVersion(findIterator->entryValue()))
+ {
+ skipInstall = EFalse;
+ }
+ else
+ {
+ LOG1WSTR(EJavaPreinstaller, EInfo,
+ "CheckWhichAppsShouldBeInstalledL: Application %s "
+ "has already been installed", desToWstring(namePtr));
+ }
+ }
+ else
+ {
+ skipInstall = EFalse;
+ ELOG1WSTR(EJavaPreinstaller,
+ "CheckWhichAppsShouldBeInstalledL: No version info "
+ "in Storage for application %s", desToWstring(namePtr));
+ }
+ }
+ }
+ else
+ {
+ skipInstall = EFalse;
+ LOG(EJavaPreinstaller, EInfo,
+ "CheckWhichAppsShouldBeInstalledL: Application has not "
+ "been installed previously");
+ }
+
+ foundEntries.clear();
+ }
+ else
+ {
+ // If Jad parsing fails don't preinstall this
+ skipInstall = ETrue;
+ TPtr16 ptrJadName = iJadFiles[i]->Des();
+ ELOG1WSTR(EJavaPreinstaller,
+ "CheckWhichAppsShouldBeInstalledL: Parsing JAD %s failed",
+ desToWstring(ptrJadName));
+ }
+
+ if (skipInstall)
+ {
+ delete iJadFiles[i];
+ iJadFiles[i] = NULL;
+ }
+ else
+ {
+ iNumberOfAppsToInstall++;
+ }
+ }
+
+ js->close();
+}
+
+/**
+ * Adds the preinstall directory of every local, non-substed drive to iDirs
+ */
+void CSilentMIDletInstall::GetDirsToBeScannedL()
+{
+ TChar driveChar;
+
+ for (TInt drive = 0; drive < KMaxDrives; drive++)
+ {
+ // All present local drives are scanned for
+ // java applications to be preinstalled
+ if (iDriveStatuses[drive] & DriveInfo::EDrivePresent)
+ {
+ // The preinstall directory in this drive must be scanned.
+ // Reserve memory also for drive letter and terminating zero
+ // for logging.
+ HBufC *preinstallDir = HBufC::NewLC(KPreinstallDir().Length() + 2);
+ TPtr dirPtr(preinstallDir->Des());
+
+ (void)iFs.DriveToChar(drive, driveChar);
+ dirPtr.Append(driveChar);
+ dirPtr.Append(KPreinstallDir());
+
+ // Add new search directory
+ iDirs.AppendL(preinstallDir);
+ CleanupStack::Pop(preinstallDir);
+ }
+ }
+}
+
+/**
+ * Checks all local drives in the device and stores the DriveInfo API drive
+ * status information for each drive to iDriveStatuses
+ * @exception Cannot get drive list.
+ */
+void CSilentMIDletInstall::GetAllDeviceDrivesL()
+{
+ JELOG2(EJavaPreinstaller);
+
+ TDriveList driveList;
+ // get all drives
+ TInt err = iFs.DriveList(driveList);
+ if (KErrNone != err)
+ {
+ ELOG1(EJavaPreinstaller,
+ "GetAllDeviceDrives cannot get drive list, err %d", err);
+ User::Leave(err);
+ }
+
+ // store status of the non-remote, non-substed drives
+ TUint status = 0;
+ for (TInt drive = 0; drive < KMaxDrives; drive++)
+ {
+ iDriveStatuses[drive] = 0;
+
+ if (driveList[drive] == 0)
+ {
+ // no such drive in this device
+ continue;
+ }
+
+ err = DriveInfo::GetDriveStatus(iFs, drive, status);
+ if (KErrNone != err)
+ {
+ ELOG2(EJavaPreinstaller,
+ "GetAllDeviceDrivesL cannot get drive %d status, err %d",
+ drive, err);
+ User::Leave(err);
+ }
+ // D drive is temporary RAM drive, skip it
+ // Drives J to Y are substed or remote drives, skip them
+ if ((drive == EDriveD) || ((drive >= EDriveJ) && (drive <= EDriveY)))
+ {
+ continue;
+ }
+
+ iDriveStatuses[drive] = status;
+ }
+}
+
+/**
+ * Reads the whole content of the Jad file and returns it in
+ * buffer in Symbian Unicode character set.
+ * @param[in] aJarFile
+ * @return pointer to HBufC that contains the Jad file,
+ * ownership is transferred to caller
+ * @exception If jad file content cannot be read
+ */
+HBufC *CSilentMIDletInstall::GetJadContentL(const TDesC& aJadFileName)
+{
+ JELOG2(EJavaPreinstaller);
+
+ TInt err;
+ RFile jadFile;
+
+ err = jadFile.Open(iFs, aJadFileName, EFileRead);
+ User::LeaveIfError(err);
+ CleanupClosePushL(jadFile);
+
+ // Reserve buffer for Jad in UTF-8 char set
+ TInt jadSize = 0;
+ err = jadFile.Size(jadSize);
+ User::LeaveIfError(err);
+ HBufC8 *bufUtf8Jad = HBufC8::NewL(jadSize);
+ CleanupStack::PushL(bufUtf8Jad);
+
+ // Read the content in Utf8 char set
+ TPtr8 tmpPtr(bufUtf8Jad->Des());
+ err = jadFile.Read(tmpPtr, jadSize);
+ User::LeaveIfError(err);
+
+ // Convert to Unicode
+ HBufC *bufUnicodeJad =
+ CnvUtfConverter::ConvertToUnicodeFromUtf8L(*bufUtf8Jad);
+
+ CleanupStack::PopAndDestroy(bufUtf8Jad);
+ CleanupStack::PopAndDestroy(&jadFile);
+
+ // Return to caller
+ return bufUnicodeJad;
+
+} // GetJadContentL
+
+/**
+ * Finds the java attribute specified by
+ * aAttributeName from aJad and returns the value of that attribute
+ * in HBufC.
+ * @param[in] aJad contents of Jad file
+ * @param[in] aAttributeName the name of a java attribute
+ * @return the value of the attribute. Caller gets the ownership of the
+ * returned HBufC.
+ * If the attribute is not found, returns NULL
+ */
+HBufC *CSilentMIDletInstall::ParseAttribute(const HBufC *aJad, const TDesC& aAttributeName)
+{
+ JELOG2(EJavaPreinstaller);
+
+ TInt nInd(0);
+ TBool fullNameFound(EFalse);
+ TUint32 ch;
+
+ // Start parsing from the beginning of the Jad file
+ TPtrC parsePtr = aJad->Mid(nInd);
+
+ do
+ {
+ // Find attribute name
+ nInd = parsePtr.Find(aAttributeName);
+ if (nInd < 0)
+ {
+ // Returns NULL if the attribute cannot be found
+ return NULL;
+ }
+
+ // Check that the attribute name was preceded by line break or
+ // it was at the beginning of the Jad file
+ if (nInd == 0)
+ {
+ fullNameFound = ETrue;
+ }
+ else
+ {
+ ch = parsePtr[nInd-1];
+ if ((ch == CR) || (ch == LF))
+ {
+ fullNameFound = ETrue;
+ }
+ else
+ {
+ // Name was just a part of longer string (not 'word match')
+ fullNameFound = EFalse;
+ // Skip to the last character of the found match.
+ // We can skip because we are insterested only in 'word' matches
+ // so the next cannot start inside the area we are skipping now.
+ parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length() - 1));
+ continue;
+ }
+ }
+
+ // Check whether Jad file ends after attribute name
+ if (nInd + aAttributeName.Length() >= parsePtr.Length())
+ {
+ // Jad file ends immediately after the found
+ // attribute name instance. No attribute value
+ return NULL;
+ }
+
+ // Check that there is a white space character or colon after
+ // attribute name
+ ch = parsePtr[nInd + aAttributeName.Length()];
+ if ((ch == COLON) || (ch == SP) || (ch == HT))
+ {
+ fullNameFound = ETrue;
+ }
+ else
+ {
+ // Name was just a part of longer string (not 'word match')
+ fullNameFound = EFalse;
+ // Skip to the next character after the found match
+ parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length()));
+ continue;
+ }
+ }
+ while (!fullNameFound);
+
+ // Skip to the end of the attribute name and find ':' after the name.
+ // The skipped characters must be white space chacraters, otherwise
+ // the attribute name is illegal and Java Installer will not accept
+ // the Jad file.
+ parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length() - 1));
+ nInd = parsePtr.Locate(COLON);
+ if (nInd < 0)
+ {
+ return NULL;
+ }
+ nInd++;
+
+ // Parse attribute value (CR or LF ends)
+ TInt nEndInd = parsePtr.Locate(CR);
+ TInt nTmpInd = parsePtr.Locate(LF);
+
+ if (KErrNotFound == nEndInd)
+ {
+ nEndInd = parsePtr.Length() - 1;
+ }
+ if (KErrNotFound == nTmpInd)
+ {
+ nTmpInd = parsePtr.Length() - 1;
+ }
+
+ if (nTmpInd < nEndInd)
+ {
+ nEndInd = nTmpInd;
+ }
+
+ if (nEndInd < nInd)
+ {
+ return NULL;
+ }
+
+ TPtrC attributeValue = parsePtr.Mid(nInd, (nEndInd - nInd));
+
+ // Remove possible white space from the beginning and end of the value
+ HBufC *bufValue = attributeValue.Alloc();
+ if (NULL == bufValue)
+ {
+ return NULL;
+ }
+ TPtr value = bufValue->Des();
+ value.Trim();
+
+ return bufValue;
+} // parseAttribute
+
+/**
+ * Parses the application version string given in aAppVersionString
+ * and returns the corresponding Symbian TAppVersion.
+ * @param[in] aAppVersionString version string to be parsed
+ * @return application version object
+ * If parsing cannot be done, returns application version 0.0.0
+ */
+TAppVersion CSilentMIDletInstall::DesToAppVersion(const HBufC *aAppVersionString)
+{
+ JELOG2(EJavaPreinstaller);
+
+ TInt err;
+ TAppVersion midletVersion;
+ TAppVersion midletVersionZero;
+ TLex versionParser(*aAppVersionString);
+ err = versionParser.Val(midletVersion.iMajor);
+ if (KErrNone != err)
+ {
+ WLOG1(EJavaPreinstaller,
+ "DesToAppVersion cannot parse midlet major version, error %d", err);
+ return midletVersionZero;
+ }
+
+ TChar dot = versionParser.Get();
+ if (dot != '.')
+ {
+ WLOG(EJavaPreinstaller,
+ "DesToAppVersion suspicious midlet version, no dot after major version");
+ // return at least the major version
+ return midletVersion;
+ }
+ err = versionParser.Val(midletVersion.iMinor);
+ if (KErrNone != err)
+ {
+ WLOG1(EJavaPreinstaller,
+ "DesToAppVersion cannot parse midlet minor version, error %d", err);
+ // return at least the major version
+ return midletVersion;
+ }
+
+ dot = versionParser.Get();
+ if (dot != '.')
+ {
+ // return the major and minor version
+ return midletVersion;
+ }
+ err = versionParser.Val(iMIDletVersion.iBuild);
+ if (KErrNone != err)
+ {
+ WLOG1(EJavaPreinstaller,
+ "DesToAppVersion cannot parse midlet build version, error %d", err);
+ // return at least the major and minor version
+ return midletVersion;
+ }
+
+ if (!versionParser.Eos())
+ {
+ WLOG(EJavaPreinstaller,
+ "DesToAppVersion suspicious midlet version, end has extra non number characters");
+ }
+
+ return midletVersion;
+} // DesToAppVersion