/**
 * Gets the settings of a MIDlet suite from persistent storage.
 * <pre>
 * The format of the properties file will be:
 *
 *   push interrupt setting as an jbyte
 *   length of a permissions as an int
 *   array of permissions jbytes
 *   push options as jint
 * </pre>
 *
 * @param ppszError pointer to character string pointer to accept an error
 * @param suiteId ID of the suite
 * @param pEnabled pointer to an enabled setting
 *
 * @return error code (ALL_OK if successful)
 */
MIDPError
read_enabled_state(char** ppszError, SuiteIdType suiteId,
                   jboolean* pEnabled) {
    MidletSuiteData* pData;
    MIDPError status;

    /* load _suites.dat */
    status = read_suites_data(ppszError);
    if (status != ALL_OK) {
        return status;
    }

    pData = g_pSuitesData;
    *pEnabled = 0;

    /* try to find a suite */
    while (pData != NULL) {
        if (pData->suiteId == suiteId) {
            *pEnabled = pData->isEnabled;
            break;
        }

        pData = pData->nextEntry;
    }

    return ALL_OK;
}
예제 #2
0
/**
 * Tells if a given suite is in a list of the installed suites.
 *
 * @param suiteId unique ID of the midlet suite
 *
 * @return ALL_OK if the suite is in the list of the installed suites,
 *         NOT_FOUND if not,
 *         IO_ERROR if an i/o error occured when reading the information
 *         about the installed suites,
 *         OUT_OF_MEMORY if out of memory or IO error,
 *         SUITE_CORRUPTED_ERROR is suite is found in the list, but it's
 *         corrupted.
 */
static MIDPError
suite_in_list(SuiteIdType suiteId) {
    MIDPError status;
    char* pszError;
    MidletSuiteData* pData;

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);
    if (status != ALL_OK) {
        return status;
    }

    pData = get_suite_data(suiteId);

    if (pData != NULL) {
        /*
         * Make sure that suite is not corrupted. Return
         * SUITE_CORRUPTED_ERROR if the suite is corrupted.
         * Remove the suite before returning the status.
         */
        status = check_for_corrupted_suite(suiteId);
    } else {
        status = NOT_FOUND;
    }

    return status;
}
예제 #3
0
/**
 * Retrieves an ID of the storage where the midlet suite with the given suite ID
 * is stored.
 *
 * @param suiteId The application suite ID
 * @param pSuiteId [out] receives an ID of the storage where the suite is stored
 *
 * @return error code (ALL_OK if successful)
 */
MIDPError
midp_suite_get_suite_storage(SuiteIdType suiteId, StorageIdType* pStorageId) {
    MIDPError status;
    MidletSuiteData* pData;
    char* pszError;

    if (pStorageId == NULL) {
        return BAD_PARAMS;
    }

    if (suiteId == INTERNAL_SUITE_ID) {
        /* handle a special case: predefined suite ID is given */
        *pStorageId = INTERNAL_STORAGE_ID;
        return ALL_OK;
    }

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);

    if (status == ALL_OK) {
        pData = get_suite_data(suiteId);
        if (pData) {
            *pStorageId = pData->storageId;
        } else {
            *pStorageId = UNUSED_STORAGE_ID;
            status = NOT_FOUND;
        }
    }

    return status;
}
예제 #4
0
/**
 * If the suite exists, this function returns a unique identifier of
 * MIDlet suite. Note that suite may be corrupted even if it exists.
 * If the suite doesn't exist, a new suite ID is created.
 *
 * @param vendor name of the vendor that created the application, as
 *          given in a JAD file
 * @param name name of the suite, as given in a JAD file
 * @param pSuiteId [out] receives the platform-specific suite ID of the
 *          application given by vendorName and appName, or string with
 *          a null data if suite does not exist, or
 *          out of memory error occured, or suite is corrupted.
 *
 * @return  ALL_OK if suite found,
 *          NOT_FOUND if suite does not exist (so a new ID was created),
 *          other error code in case of error
 */
MIDPError
midp_get_suite_id(const pcsl_string* vendor, const pcsl_string* name,
                  SuiteIdType* pSuiteId) {
    MIDPError status;
    char *pszError;
    MidletSuiteData* pData;

    *pSuiteId = UNUSED_SUITE_ID;

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);
    if (status != ALL_OK) {
        return status;
    }

    pData = g_pSuitesData;

    /* try to find a suite */
    while (pData != NULL) {
        if (pcsl_string_equals(&pData->varSuiteData.suiteName, name) &&
                pcsl_string_equals(&pData->varSuiteData.suiteVendor, vendor)) {
            *pSuiteId = pData->suiteId;
            return ALL_OK; /* IMPL_NOTE: consider SUITE_CORRUPTED_ERROR */
        }

        pData = pData->nextEntry;
    }

    /* suite was not found - create a new suite ID */
    status = midp_create_suite_id(pSuiteId);

    return (status == ALL_OK) ? NOT_FOUND : status;
}
/**
 * Retrieves the number of the installed components belonging
 * to the given midlet suite.
 *
 * @param suiteId          [in]  ID of the MIDlet suite the information about
 *                               whose components must be retrieved
 * @param pNumOfComponents [out] pointer to variable to accept the number
 *                               of components
 *
 * @returns error code (ALL_OK if no errors)
 */
MIDPError
midp_get_number_of_components(SuiteIdType suiteId, int* pNumOfComponents) {
    MIDPError status;
    char* pszError;
    MidletSuiteData* pData;
    int n = 0;

    do {
        if (midpInit(LIST_LEVEL) != 0) {
            status = OUT_OF_MEMORY;
            break;
        }

        /* load _suites.dat */
        status = read_suites_data(&pszError);
        if (status != ALL_OK) {
            storageFreeError(pszError);
            break;
        }

        pData = g_pSuitesData;

        /* walk through the linked list */
        while (pData != NULL) {
            if (pData->suiteId == suiteId && pData->type == COMPONENT_DYNAMIC) {
                n++;
            }
            pData = pData->nextEntry;
        }

        *pNumOfComponents = n;
    } while(0);

    return status;
}
/*
 * Reads information about the installed midlet suite's component
 * from the storage.
 *
 * @param componentId unique ID of the component
 * @param ci ComponentInfo object to fill with the information about
 *           the midlet suite's component having the given ID
 *
 * @exception IOException if an the information cannot be read
 * @exception IllegalArgumentException if suiteId is invalid or ci is null
 */
KNIEXPORT KNI_RETURNTYPE_VOID
KNIDECL(
    com_sun_midp_midletsuite_DynamicComponentStorage_getComponentInfo) {
    ComponentIdType componentId = KNI_GetParameterAsInt(1);
    MIDPError status = ALL_OK;

    KNI_StartHandles(3);
    KNI_DeclareHandle(componentInfoObject);
    KNI_DeclareHandle(componentInfoClass);
    KNI_DeclareHandle(tmpHandle);

    KNI_GetParameterAsObject(2, componentInfoObject);
    KNI_GetObjectClass(componentInfoObject, componentInfoClass);

    do {
        char *pszError = NULL;
        MidletSuiteData *pData = NULL;

        /* Ensure that suite data are read */
        status = read_suites_data(&pszError);
        storageFreeError(pszError);
        if (status != ALL_OK) {
            break;
        }

        pData = get_component_data(componentId);
        if (!pData) {
            status = NOT_FOUND;
            break;
        }

        KNI_RESTORE_INT_FIELD(componentInfoObject, componentInfoClass,
                              "componentId", componentId);
        KNI_RESTORE_INT_FIELD(componentInfoObject, componentInfoClass,
                              "suiteId", pData->suiteId);
        KNI_RESTORE_PCSL_STRING_FIELD(componentInfoObject, componentInfoClass,
                                     "displayName",
                                      &(pData->varSuiteData.displayName),
                                      tmpHandle);
        KNI_RESTORE_PCSL_STRING_FIELD(componentInfoObject, componentInfoClass,
                                     "version",
                                      &(pData->varSuiteData.suiteVersion),
                                      tmpHandle);
        KNI_RESTORE_BOOLEAN_FIELD(componentInfoObject, componentInfoClass,
                                  "trusted", pData->isTrusted);
    } while (0);

    if (status != ALL_OK) {
        if (status == NOT_FOUND) {
            KNI_ThrowNew(midpIllegalArgumentException, "bad component ID");
        } else {
            KNI_ThrowNew(midpIOException, NULL);
        }
    }

    KNI_EndHandles();
    KNI_ReturnVoid();
}
/**
 * Checks the integrity of the suite storage database and of the
 * installed suites.
 *
 * @param fullCheck 0 to check just an integrity of the database,
 *                    other value for full check
 * @param delCorruptedSuites != 0 to delete the corrupted suites,
 *                           0 - to keep them (for re-installation).
 *
 * @return ALL_OK if no errors,
 *         SUITE_CORRUPTED_ERROR if the suite database was corrupted
 *                               but has been successfully repaired,
 *         another error code if the database is corrupted and
 *         could not be repaired
 */
MIDPError
midp_check_suites_integrity(int fullCheck, int delCorruptedSuites) {
    MIDPError status;
    char *pszError = NULL;
    int dbWasCorrupted = 0;

    /* Check if there is a previously started transaction exists. */
    if (unfinished_transaction_exists()) {
        (void)rollback_transaction();
    }

    /* Check if the suite database is corrupted and repair it if needed. */
    status = read_suites_data(&pszError);
    if (status == SUITE_CORRUPTED_ERROR) {
        dbWasCorrupted = 1;
        status = repair_suite_db();
    }
    if (status != ALL_OK) {
        /* give up, user interaction is needed */
        return status;
    }

    /* if fullCheck is true, check all installed suites */
    if (fullCheck) {
        int i, numOfSuites;
        SuiteIdType suiteId, *pSuiteIds = NULL;

        status = midp_get_suite_ids(&pSuiteIds, &numOfSuites);

        if (status == ALL_OK) {
            for (i = 0; i < numOfSuites; i++) {
                suiteId = pSuiteIds[i];

                if (check_for_corrupted_suite(suiteId) ==
                        SUITE_CORRUPTED_ERROR) {
                    dbWasCorrupted = 1;
                    if (delCorruptedSuites) {
                        midp_remove_suite(suiteId);
                    }
                }
            }

            if (pSuiteIds != NULL) {
                midp_free_suite_ids(pSuiteIds, numOfSuites);
            }
        }
    }

    return dbWasCorrupted ? SUITE_CORRUPTED_ERROR : ALL_OK;
}
/**
 * Tells if a given suite is in a list of the installed suites.
 *
 * @param suiteId unique ID of the midlet suite
 *
 * @return ALL_OK if the suite is in the list of the installed suites,
 *         NOT_FOUND if not,
 *         IO_ERROR if an i/o error occured when reading the information
 *         about the installed suites,
 *         OUT_OF_MEMORY if out of memory or IO error,
 *         SUITE_CORRUPTED_ERROR is suite is found in the list, but it's
 *         corrupted.
 */
static MIDPError
suite_in_list(ComponentType type, SuiteIdType suiteId,
              ComponentIdType componentId) {
    MIDPError status;
    char* pszError;
    MidletSuiteData* pData;

#if !ENABLE_DYNAMIC_COMPONENTS
    /** to supress compilation warnings */
    (void)type;
    (void)componentId;
#endif

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);
    if (status != ALL_OK) {
        return status;
    }

#if ENABLE_DYNAMIC_COMPONENTS
    if (type == COMPONENT_DYNAMIC) {
        pData = get_component_data(componentId);
        if (pData == NULL) {
            status = NOT_FOUND;
        }
    } else {
#endif /* ENABLE_DYNAMIC_COMPONENTS */
        pData = get_suite_data(suiteId);

        if (pData != NULL) {
            /*
             * Make sure that suite is not corrupted. Return
             * SUITE_CORRUPTED_ERROR if the suite is corrupted.
             * Remove the suite before returning the status.
             */
            status = check_for_corrupted_suite(suiteId);
        } else {
            status = NOT_FOUND;
        }
#if ENABLE_DYNAMIC_COMPONENTS
    }
#endif

    return status;
}
/**
 * Retrieves the number of installed midlet suites.
 *
 * @param pNumOfSuites [out] pointer to variable to accept the number of suites
 *
 * @returns error code (ALL_OK if no errors)
 */
MIDPError
midp_get_number_of_suites(int* pNumOfSuites) {
    MIDPError status;
    char* pszError;

    do {
        /*
         * This is a public API which can be called without the VM running
         * so we need automatically init anything needed, to make the
         * caller's code less complex.
         *
         * Initialization is performed in steps so that we do use any
         * extra resources such as the VM for the operation being performed.
         */
        if (midpInit(LIST_LEVEL) != 0) {
            status = OUT_OF_MEMORY;
            break;
        }

        /* load _suites.dat */
        status = read_suites_data(&pszError);
        if (status == ALL_OK) {
#if ENABLE_DYNAMIC_COMPONENTS
            MidletSuiteData* pData = g_pSuitesData;
            int num = 0;

            /* walk through the linked list */
            while (pData != NULL) {
                if (pData->type == COMPONENT_REGULAR_SUITE) {
                    num++;
                }
                pData = pData->nextEntry;
            }

            *pNumOfSuites = num;
#else
            *pNumOfSuites = g_numberOfSuites;
#endif /* ENABLE_DYNAMIC_COMPONENTS */
        } else {
            storageFreeError(pszError);
        }
    } while(0);

    return status;
}
/**
 * Retrieves an ID of the requested type for the given suite.
 *
 * @param suiteId The application suite ID
 * @param resultType 0 to return suite storage ID, 1 - suite folder ID
 * @param pResult [out] receives the requested ID
 *
 * @return error code (ALL_OK if successful)
 */
static MIDPError
get_suite_int_impl(SuiteIdType suiteId, int resultType, void* pResult) {
    MIDPError status;
    MidletSuiteData* pData;
    char* pszError;

    if (pResult == NULL) {
        return BAD_PARAMS;
    }

    if (suiteId == INTERNAL_SUITE_ID) {
        /* handle a special case: predefined suite ID is given */
        if (resultType) {
            *(FolderIdType*)pResult  = 0;
        } else {
            *(StorageIdType*)pResult = INTERNAL_STORAGE_ID;
        }
        return ALL_OK;
    }

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);

    if (status == ALL_OK) {
        pData = get_suite_data(suiteId);
        if (pData) {
            if (resultType) {
                *(FolderIdType*)pResult  = pData->folderId;
            } else {
                *(StorageIdType*)pResult = pData->storageId;
            }
        } else {
            if (resultType) {
                *(FolderIdType*)pResult  = -1;
            } else {
                *(StorageIdType*)pResult = UNUSED_STORAGE_ID;
            }
            status = NOT_FOUND;
        }
    }

    return status;
}
/**
 * Checks if a midlet suite or dynamic component with the given name
 * created by the given vendor exists.
 * If it does, this function returns a unique identifier of the suite
 * or component. Note that the suite or component may be corrupted even
 * if it exists. If it doesn't, a new suite ID or component ID is created.
 *
 * @param type type of the component
 * @param suiteId if type == COMPONENT_DYNAMIC, contains ID of the suite this
 *                components belongs to; unused otherwise
 * @param vendor name of the vendor that created the application or component,
 *        as given in a JAD file
 * @param name name of the suite or component, as given in a JAD file
 * @param pId [out] receives the platform-specific suite ID or component ID
 *        of the application given by vendor and name, or UNUSED_SUITE_ID /
 *        UNUSED_COMPONENT_ID if suite does not exist, or out of memory
 *        error occured, or the suite / component is corrupted.
 *
 * @return  ALL_OK if suite or component found,
 *          NOT_FOUND if suite or component does not exist (so a new ID
 *          was created), other error code in case of error
 */
static MIDPError
get_suite_or_component_id(ComponentType type, SuiteIdType suiteId,
                           const pcsl_string* vendor, const pcsl_string* name,
                           jint* pId) {
    MIDPError status;
    char *pszError;
    MidletSuiteData* pData;

#if ENABLE_DYNAMIC_COMPONENTS
    if (type != COMPONENT_DYNAMIC) {
        *pId = (jint)UNUSED_SUITE_ID;
    } else {
        *pId = (jint)UNUSED_COMPONENT_ID;
    }
#else
    (void)type;
    (void)suiteId;
    *pId = (jint)UNUSED_SUITE_ID;
#endif /* ENABLE_DYNAMIC_COMPONENTS */

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);
    if (status != ALL_OK) {
        return status;
    }

    pData = g_pSuitesData;

    /* try to find a suite */
    while (pData != NULL) {
        if (pcsl_string_equals(&pData->varSuiteData.suiteName, name) &&
                pcsl_string_equals(&pData->varSuiteData.suiteVendor, vendor)
#if ENABLE_DYNAMIC_COMPONENTS
                    && (type == pData->type) && (type != COMPONENT_DYNAMIC ||
                        (type == COMPONENT_DYNAMIC && suiteId == pData->suiteId))
#endif
        ) {
#if ENABLE_DYNAMIC_COMPONENTS
            if (type != COMPONENT_DYNAMIC) {
                *pId = (jint)pData->suiteId;
            } else {
                *pId = (jint)pData->componentId;
            }
#else
            *pId = (jint)pData->suiteId;
#endif /* ENABLE_DYNAMIC_COMPONENTS */
            return ALL_OK; /* IMPL_NOTE: consider SUITE_CORRUPTED_ERROR */
        }

        pData = pData->nextEntry;
    }

    /* suite or component was not found - create a new suite or component ID */
#if ENABLE_DYNAMIC_COMPONENTS
    if (type != COMPONENT_DYNAMIC) {
        status = midp_create_suite_id((SuiteIdType*)pId);
    } else {
        status = midp_create_component_id((ComponentIdType*)pId);
    }
#else
    status = midp_create_suite_id((SuiteIdType*)pId);
#endif /* ENABLE_DYNAMIC_COMPONENTS */

    return (status == ALL_OK) ? NOT_FOUND : status;
}
/**
 * Moves the given midlet suite to another folder.
 *
 * @param suiteId ID of the suite
 * @param newFolderId ID of the folder where the suite must be moved
 *
 * @return ALL_OK if no errors or an error code
 */
MIDPError midp_move_suite_to_folder(SuiteIdType suiteId,
                                    FolderIdType newFolderId) {
    MIDPError status = ALL_OK;
    char* pszError = NULL;
    lockStorageList *node = NULL;
    MidletSuiteData* pSuiteData;
    FolderIdType oldFolderId;

    /*
     * This is a public API which can be called without the VM running
     * so we need automatically init anything needed, to make the
     * caller's code less complex.
     *
     * Initialization is performed in steps so that we do use any
     * extra resources such as the VM for the operation being performed.
     */
    if (midpInit(REMOVE_LEVEL) != 0) {
        return OUT_OF_MEMORY;
    }

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);

    if (status != ALL_OK) {
        return status;
    }

    node = find_storage_lock(suiteId);
    if (node != NULL) {
        if (node->update != KNI_TRUE) {
            return SUITE_LOCKED;
        }
    }

    pSuiteData = get_suite_data(suiteId);
    if (pSuiteData == NULL) {
        remove_storage_lock(suiteId);
        return NOT_FOUND;
    }

    status = begin_transaction(TRANSACTION_MOVE_TO_FOLDER, suiteId, NULL);
    if (status != ALL_OK) {
        remove_storage_lock(suiteId);
        return status;
    }

    oldFolderId = pSuiteData->folderId;
    pSuiteData->folderId = newFolderId;

    status = write_suites_data(&pszError);
    storageFreeError(pszError);

    if (status != ALL_OK) {
        pSuiteData->folderId = oldFolderId;
        (void)rollback_transaction();
    } else {
        (void)finish_transaction();
    }

    remove_storage_lock(suiteId);

    return status;
}
/**
 * Removes a software package given its suite ID
 * <p>
 * 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.
 *
 * @param suiteId ID of the suite
 *
 * @return ALL_OK if no errors,
 *         NOT_FOUND if the suite does not exist,
 *         SUITE_LOCKED if the suite is locked,
 *         BAD_PARAMS this suite cannot be removed
 */
MIDPError
midp_remove_suite(SuiteIdType suiteId) {
    pcsl_string filename;
    char* pszError;
    pcsl_string suiteRoot;
    MIDPError status;
    int operationStarted = 0;
    void* fileIteratorHandle = NULL;
    MidpProperties properties;
    pcsl_string* pproperty;
    MidletSuiteData* pData = NULL;
    pcsl_string filenameBase;

    lockStorageList *node = NULL;

    /* get the filename base from the suite id */
    status = build_suite_filename(suiteId, &PCSL_STRING_EMPTY, 
                                          &filenameBase);
    if (status != ALL_OK) {
        return status;
    }
    node = find_storage_lock(suiteId);
    if (node != NULL) {
        if (node->update != KNI_TRUE) {
            return SUITE_LOCKED;
        }
    }

    /*
     * This is a public API which can be called without the VM running
     * so we need automatically init anything needed, to make the
     * caller's code less complex.
     *
     * Initialization is performed in steps so that we do use any
     * extra resources such as the VM for the operation being performed.
     */
    if (midpInit(REMOVE_LEVEL) != 0) {
        return OUT_OF_MEMORY;
    }

    do {
        int rc; /* return code for rmsdb_... and storage_... */

        /* load _suites.dat */
        status = read_suites_data(&pszError);
        storageFreeError(pszError);
        if (status != ALL_OK) {
            break;
        }

        /* check that the suite exists and it is not a preloaded one */
        pData = get_suite_data(suiteId);

        if (pData == NULL) {
            status = NOT_FOUND;
            break;
        }

        /* notify the listeners that we starting to remove the suite */
        operationStarted = 1;
        suite_listeners_notify(SUITESTORE_LISTENER_TYPE_REMOVE,
            SUITESTORE_OPERATION_START, ALL_OK, pData);

        if (pData->type == COMPONENT_PREINSTALLED_SUITE) {
            status = BAD_PARAMS;
            break;
        }

        status = begin_transaction(TRANSACTION_REMOVE_SUITE, suiteId, NULL);
        if (status != ALL_OK) {
            return status;
        }

        /*
         * Remove the files
         * Call the native RMS method to remove the RMS data.
         * This function call is needed for portability
         */
        rc = rmsdb_remove_record_stores_for_suite(&filenameBase, suiteId);
        if (rc == KNI_FALSE) {
            status = SUITE_LOCKED;
            break;
        }

        pushdeletesuite(suiteId);

        /*
         * If there is a delete notify property, add the value to the delete
         * notify URL list.
         */
        properties = midp_get_suite_properties(suiteId);
        if (properties.numberOfProperties > 0) {
            pproperty = midp_find_property(&properties, &DELETE_NOTIFY_PROP);
            if (pcsl_string_length(pproperty) > 0) {
                midpAddDeleteNotification(suiteId, pproperty);
            }

            pproperty = midp_find_property(&properties, &INSTALL_NOTIFY_PROP);
            if (pcsl_string_length(pproperty) > 0) {
                /*
                 * Remove any pending install notifications since they are only
                 * retried when the suite is run.
                 */
                midpRemoveInstallNotification(suiteId);
            }

            midp_free_properties(&properties);
        }

        if ((status = get_suite_storage_root(suiteId, &suiteRoot)) != ALL_OK) {
            break;
        }

        fileIteratorHandle = storage_open_file_iterator(&suiteRoot);
        if (!fileIteratorHandle) {
            status = IO_ERROR;
            break;
        }

#if ENABLE_ICON_CACHE
        midp_remove_suite_icons(suiteId);
#endif        

        for (;;) {
            rc = storage_get_next_file_in_iterator(&suiteRoot,
                fileIteratorHandle, &filename);
            if (0 != rc) {
                break;
            }
            storage_delete_file(&pszError, &filename);
            pcsl_string_free(&filename);
            if (pszError != NULL) {
                storageFreeError(pszError);
                break;
            }
        }

    } while (0);

    pcsl_string_free(&suiteRoot);
    storageCloseFileIterator(fileIteratorHandle);

    (void)finish_transaction();

    /*
     * Notify the listeners the we've finished removing the suite.
     * It should be done before remove_from_suite_list_and_save()
     * call because it frees pData structure.
     */
    if (operationStarted) {
        suite_listeners_notify(SUITESTORE_LISTENER_TYPE_REMOVE,
            SUITESTORE_OPERATION_END, status, pData);
    }

    if (status == ALL_OK) {
        (void)remove_from_suite_list_and_save(suiteId);
    }

    remove_storage_lock(suiteId);

    return status;
}
/**
 * Get the list installed of MIDlet suite IDs.
 *
 * Note that memory for the suite IDs is allocated by the callee,
 * and the caller is responsible for freeing it using midp_free_suite_ids().
 *
 * @param ppSuites empty array of jints to fill with suite IDs
 * @param pNumOfSuites [out] pointer to variable to accept the number
 * of suites in the returned array
 *
 * @returns error code: ALL_OK if no errors,
 *          OUT_OF_MEMORY if for out of memory,
 *          IO_ERROR if an IO error
 */
MIDPError
midp_get_suite_ids(SuiteIdType** ppSuites, int* pNumOfSuites) {
    MIDPError status;
    char* pszError;
    SuiteIdType* pSuiteIds;
    MidletSuiteData* pData;
    int numberOfSuites = 0;
#if ENABLE_DYNAMIC_COMPONENTS
    int numberOfEntries = 0;
#endif

    *ppSuites = NULL;
    *pNumOfSuites = 0;

    /*
     * This is a public API which can be called without the VM running
     * so we need automatically init anything needed, to make the
     * caller's code less complex.
     *
     * Initialization is performed in steps so that we do use any
     * extra resources such as the VM for the operation being performed.
     */
    if (midpInit(LIST_LEVEL) != 0) {
        return OUT_OF_MEMORY;
    }

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);

    if (status != ALL_OK) {
        return status;
    }

    if (!g_numberOfSuites) {
        /* there are no installed suites */
        return ALL_OK;
    }

    pData = g_pSuitesData;

    /* allocate a memory for the IDs */
    pSuiteIds = pcsl_mem_malloc(g_numberOfSuites * sizeof(SuiteIdType));
    if (pSuiteIds == NULL) {
        return OUT_OF_MEMORY;
    }

    /* walk through the linked list collecting suite IDs */
    while (pData != NULL) {
#if ENABLE_DYNAMIC_COMPONENTS
        if (pData->type == COMPONENT_REGULAR_SUITE) {
#endif
            pSuiteIds[numberOfSuites] = pData->suiteId;
            numberOfSuites++;
#if ENABLE_DYNAMIC_COMPONENTS
        }
        numberOfEntries++;
#endif
        pData = pData->nextEntry;
    }

#if ENABLE_DYNAMIC_COMPONENTS
    if (numberOfEntries != g_numberOfSuites) {
#else
    if (numberOfSuites != g_numberOfSuites) {
#endif
        /*
         * This should not happen: it means that something is wrong with
         * the list of structures containing the midlet suites information.
         */
        pcsl_mem_free(pSuiteIds);
        return IO_ERROR;
    }

    *ppSuites = pSuiteIds;
    *pNumOfSuites = numberOfSuites;

    return ALL_OK;
}

/**
 * Frees a list of suite IDs.
 *
 * @param pSuiteIds point to an array of suite IDs
 * @param numberOfSuites number of elements in pSuites
 */
void
midp_free_suite_ids(SuiteIdType* pSuiteIds, int numberOfSuites) {
    (void)numberOfSuites;
    pcsl_mem_free(pSuiteIds);
}