From: Ernest Borowski Date: Thu, 9 Aug 2018 14:52:00 +0000 (+0200) Subject: [PPMTester] Add test for checking & requesting multiple privileges using X-Git-Tag: submit/tizen/20180831.110312~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d1ceb9ae7438cd3967a3236c8ab2b11e2d3362c9;p=platform%2Fcore%2Fsecurity%2Faskuser.git [PPMTester] Add test for checking & requesting multiple privileges using single api call. Change-Id: If5b34458afe097da456d27dcd41fb4962aacccea Signed-off-by: Ernest Borowski --- diff --git a/test/apps/capi/PPMTester/src/ppmtester.c b/test/apps/capi/PPMTester/src/ppmtester.c index efbc62c..4d81cc1 100644 --- a/test/apps/capi/PPMTester/src/ppmtester.c +++ b/test/apps/capi/PPMTester/src/ppmtester.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,48 +17,17 @@ /** * @file ppmtester.c * @author Pawel Kowalski + * @author Ernest Borowski * @brief The implementation of testing application of the PPM CAPI. */ -#include -#include -#include -#include -#include -#include -#include - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "ppmtester" - -#define BUFFER_SIZE 1024 - -#if !defined(PACKAGE) -#define PACKAGE "org.example.ppmtester" -#endif - -typedef struct appdata -{ - Evas_Object *window; - Evas_Object *conform; - Evas_Object *navi; - Evas_Object *msg_box; -} appdata_s; +#include "ppmtester.h" static void win_delete_request_cb(void *data, Evas_Object *obj, void *event_info) { ui_app_exit(); } -/** - * @brief Prints massage into the message box. - * - * @param[in] obj The message box. - * @param[in] msg The text message that is written to the message box. - * @param[in] ... Additional arguments declared in the text message. - */ void print_msg(Evas_Object *obj, const char* msg, ...) { va_list args; @@ -75,22 +44,12 @@ void print_msg(Evas_Object *obj, const char* msg, ...) elm_scroller_region_show(obj, 0, c_y, 0, 0); } -/** - * @brief Clears the message box. - * - * @param[in] data User specific data. - * @param[in] btn Pointer to the button object. - * @param[in] event_info Additional event information. - */ static void _btn_clear_cb(void *data, Evas_Object *btn, void *event_info) { appdata_s *ad = data; elm_entry_entry_set(ad->msg_box, ""); } -/** - * @brief Creates the display object. - */ Evas_Object *_create_new_cd_display(appdata_s *ad, const char *name, void *cb) { // Create a scroller @@ -178,9 +137,6 @@ Eina_Bool _pop_cb(void *data, Elm_Object_Item *item) return EINA_FALSE; } -/** - * @brief Function copied from the Account Manager example. - */ Evas_Object *_new_button(appdata_s *ad, Evas_Object *display, const char *name, void *cb) { // Create a button @@ -194,54 +150,53 @@ Evas_Object *_new_button(appdata_s *ad, Evas_Object *display, const char *name, return bt; } -/** - * @brief Writes text into dlogutil and into GUI. - * - * @details In order to read a message using dlogutil, - * type in the emulator's terminal: dlogutil ppmtester - * The 'ppmtester' flag is set using LOG_TAG constant. - * - * @param[in] ad The structure with GUI elements. - * @param[in] privilege A privilege that is to be logged. - * @param[in] msg A text that is to be written. - */ -void log_privilege (appdata_s *ad, const char* privilege, const char* msg) +void log_privilege(appdata_s *ad, const char* privilege, const char* msg) { - print_msg(ad->msg_box, "----------
Privilege: %s
Message: %s", privilege, msg); - dlog_print(DLOG_INFO, LOG_TAG, "----------\nPrivilege: %s\nMessage: %s", privilege, msg); + if (msg == NULL) + return; + if (privilege != NULL) + { + print_msg(ad->msg_box, "----------
Privilege: %s
Message: %s", privilege, msg); + dlog_print(DLOG_INFO, LOG_TAG, "----------\nPrivilege: %s\nMessage: %s", privilege, msg); + } + else + { + print_msg(ad->msg_box, "----------

Message: %s", msg); + dlog_print(DLOG_INFO, LOG_TAG, "----------\\nMessage: %s", msg); + } } -/** - * @brief Based on ppm_popup_response_cb prototype from privacy-privilege-manager.h. - * - * @param[in] cause A value representing the reason why this callback - * has been called. - * @param[in] result A result of a response triggered by calling ppm_popup_request(). - * @param[in] privilege A privilege that has been checked. - * @param[in] user_data User specific data. - */ -void ppm_popup_response_cb_function (ppm_call_cause_e cause, ppm_request_result_e result, const char *privilege, void *user_data) +void log_result(appdata_s *ad, ppm_request_result_e result, const char *privilege) +{ + switch (result) + { + case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER: + log_privilege(ad, privilege, "A user granted permission to use a privilege for an indefinite period of time."); + break; + case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_FOREVER: + log_privilege(ad, privilege, "A user did not grant permission to use a privilege for an indefinite period of time."); + break; + case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_ONCE: + log_privilege(ad, privilege, "A user did not grant permission to use a privilege once."); + break; + default: + { + char msg[BUFFER_SIZE]; + snprintf(msg, BUFFER_SIZE, "unknown result: %d", result); + log_privilege(ad, privilege, "Unknown result: "); + break; + } + } +} + +void ppm_popup_response_cb_function(ppm_call_cause_e cause, ppm_request_result_e result, const char *privilege, void *user_data) { appdata_s *ad = user_data; switch (cause) { case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER: log_privilege(ad, privilege, "Callback was called with a valid answer."); - switch (result) - { - case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER: - log_privilege(ad, privilege, "A user granted permission to use a privilege for an indefinite period of time."); - break; - case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_FOREVER: - log_privilege(ad, privilege, "A user did not grant permission to use a privilege for an indefinite period of time."); - ui_app_exit(); - break; - case PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_ONCE: - log_privilege(ad, privilege, "A user did not grant permission to use a privilege once."); - break; - default: - break; - } + log_result(ad, result, privilege); break; case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR: log_privilege(ad, privilege, "Callback was called because of an error."); @@ -256,14 +211,50 @@ void ppm_popup_response_cb_function (ppm_call_cause_e cause, ppm_request_result_ } } -/** - * @brief Checks the privilege. Writes messages in the bottom part of the main window and - * into the shell (in order to read these messages use command: dlogutil ppmtester). - * - * @param[in] ad The structure with GUI elements. - * @param[in] privilege A privilege that is to be checked. - * @param[in] ret The code of the error. - */ +void ppm_popup_multiple_response_cb_function(ppm_call_cause_e cause, + const ppm_request_result_e *results, + const char **privileges, + size_t privileges_count, + void *user_data) +{ + static bool second_request = true; + appdata_s *ad = user_data; + switch (cause) + { + case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER: + for (int it = 0; it < privileges_count; ++it) + { + log_privilege(ad, privileges[it], "Callback was called with a valid answer."); + log_result(ad, results[it], privileges[it]); + } + break; + case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR: + for (int it = 0; it < privileges_count; ++it) + { + log_privilege(ad, privileges[it], "Callback was called because of an error."); + } + break; + default: + { + char msg[BUFFER_SIZE]; + snprintf(msg, BUFFER_SIZE, "Unknown cause: %d", cause); + for (int it = 0; it < privileges_count; ++it) + { + log_privilege(ad, privileges[it], msg); + } + break; + } + } + if (second_request) + { + second_request = false; + check_privileges(ad, privacies_for_request_in_response_callback.privileges, + sizeof(privacies_for_request_in_response_callback.privileges) / + sizeof(privacies_for_request_in_response_callback.privileges[0]), + false); + } +} + void app_log_errors(appdata_s *ad, const char* privilege, ppm_error_e ret) { switch (ret) @@ -289,14 +280,7 @@ void app_log_errors(appdata_s *ad, const char* privilege, ppm_error_e ret) } } -/** - * @brief Checks the privilege. Writes messages in the bottom part of the main window and - * into the shell (in order to read these messages use command: dlogutil ppmtester). - * - * @param[in] ad The structure with GUI elements. - * @param[in] privilege A privilege that is to be checked. - */ -void check_privilege (appdata_s *ad, const char* privilege) +void check_privilege(appdata_s *ad, const char* privilege) { ppm_check_result_e result; @@ -308,7 +292,7 @@ void check_privilege (appdata_s *ad, const char* privilege) return; } - switch (result) // Alow, deny or ask. + switch (result) // Allow, deny or ask. { case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW: log_privilege(ad, privilege, "An application has permission to use a privilege."); @@ -337,55 +321,135 @@ void check_privilege (appdata_s *ad, const char* privilege) } } -/** - * @brief Callback for 'camera' button. - * - * @param[in] ad The structure with GUI elements. - * @param[in] obj Display object. - */ +void check_privileges(appdata_s *ad, const char** privileges, size_t privileges_count, bool request_privs_ignore_ask_status) +{ + ppm_check_result_e* results = malloc(sizeof(ppm_check_result_e) * privileges_count); + if (!results) + { + print_msg(ad->msg_box, "----------
unable to allocate memory for check_results."); + dlog_print(DLOG_INFO, LOG_TAG, "----------\nunable to allocate memory for check_results."); + return; + } + + int ret = ppm_check_permissions(privileges, privileges_count, results); + + if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) // If there is an error, print log. + { + for (int it = 0; it < privileges_count; ++it) + { + app_log_errors(ad, privileges[it], ret); + } + free(results); + return; + } + + char** privs_to_ask = (char**)privileges; + if (!request_privs_ignore_ask_status) + { + privs_to_ask = malloc(sizeof(char*) * privileges_count); + } + if (!privs_to_ask) + { + print_msg(ad->msg_box, "----------
unable to allocate memory for privileges to ask."); + dlog_print(DLOG_INFO, LOG_TAG, "----------\nunable to allocate memory for privileges to ask."); + free(results); + return; + } + int askable_privs_it = 0; + for (int it = 0; it < privileges_count; ++it) + { + switch (results[it]) // Allow, deny or ask. + { + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW: + { + log_privilege(ad, privileges[it], "An application has permission to use a privilege."); + break; + } + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY: + { + log_privilege(ad, privileges[it], "An application doesn't have permission to use a privilege."); + break; + } + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK: + { + log_privilege(ad, privileges[it], "A user has to be asked whether to grant permission to use a privilege."); + if (!request_privs_ignore_ask_status) + { + privs_to_ask[askable_privs_it++] = (char*)privileges[it]; + } + break; + } + default: + { + char msg[BUFFER_SIZE]; + snprintf(msg, BUFFER_SIZE, "Unknown result: %d", results[it]); + log_privilege(ad, privileges[it], msg); + break; + } + } + } + free(results); + if (!request_privs_ignore_ask_status && askable_privs_it < 1) + { + free(privs_to_ask); + return; + } + if (request_privs_ignore_ask_status) + { + askable_privs_it = privileges_count; + } + ret = ppm_request_permissions((const char**)privs_to_ask, askable_privs_it, ppm_popup_multiple_response_cb_function, ad); + + ret = ppm_request_permissions(privileges, privileges_count, ppm_popup_multiple_response_cb_function, ad); + if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) + { + for (int it = 0; it < askable_privs_it; ++it) + { + log_privilege(ad, privs_to_ask[it], "Unable to request permissions"); + } + } + if (!request_privs_ignore_ask_status) + { + free(privs_to_ask); + } +} + void _test_privilege_camera(appdata_s *ad, Evas_Object *obj) { check_privilege(ad, "http://tizen.org/privilege/camera"); } -/** - * @brief Callback for 'location' button. - * - * @param[in] ad The structure with GUI elements. - * @param[in] obj Display object. - */ void _test_privilege_location(appdata_s *ad, Evas_Object *obj) { check_privilege(ad, "http://tizen.org/privilege/location"); } -/** - * @brief Callback for 'contact.read' button. - * - * @param[in] ad The structure with GUI elements. - * @param[in] obj Display object. - */ void _test_privilege_contact_read(appdata_s *ad, Evas_Object *obj) { check_privilege(ad, "http://tizen.org/privilege/contact.read"); } -/** - * @brief Callback for 'contact.write' button. - * - * @param[in] ad The structure with GUI elements. - * @param[in] obj Display object. - */ void _test_privilege_contact_write(appdata_s *ad, Evas_Object *obj) { check_privilege(ad, "http://tizen.org/privilege/contact.write"); } -/** - * @brief Creates buttons in the main window. - * - * @param[in] ad The structure with GUI elements. - */ +void _test_check_privacy_privileges(appdata_s *ad, Evas_Object *obj) +{ + check_privileges(ad, unique_privacies.privileges, + sizeof(unique_privacies.privileges) / + sizeof(unique_privacies.privileges[0]), + false); +} + +void _test_check_privacy_privileges_ignore_ask(appdata_s *ad, Evas_Object *obj) +{ + check_privileges(ad, unique_privacies.privileges, + sizeof(unique_privacies.privileges) / + sizeof(unique_privacies.privileges[0]), + true); +} + void create_buttons_in_main_window(appdata_s *ad) { Evas_Object *display = _create_new_cd_display(ad, "PPM Tester", _pop_cb); @@ -394,13 +458,10 @@ void create_buttons_in_main_window(appdata_s *ad) _new_button(ad, display, "location", _test_privilege_location); _new_button(ad, display, "contact.read", _test_privilege_contact_read); _new_button(ad, display, "contact.write", _test_privilege_contact_write); + _new_button(ad, display, "check.privacies", _test_check_privacy_privileges); + _new_button(ad, display, "check.privacies.ignore.ask", _test_check_privacy_privileges_ignore_ask); } -/** - * @brief Creates base GUI (window, conformant, naviframe and buttons). - * - * @param[in] ad The structure with GUI elements. - */ static void create_base_gui(appdata_s *ad) { // Setting the window diff --git a/test/apps/capi/PPMTester/src/ppmtester.h b/test/apps/capi/PPMTester/src/ppmtester.h new file mode 100644 index 0000000..455601d --- /dev/null +++ b/test/apps/capi/PPMTester/src/ppmtester.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file ppmtester.h + * @author Ernest Borowski + * @brief The header of testing application of the PPM CAPI. + */ + +#ifndef PPMTESTER_H_ +#define PPMTESTER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "ppmtester" + +#define BUFFER_SIZE 1024 + +#if !defined(PACKAGE) +#define PACKAGE "org.example.ppmtester" +#endif + +typedef struct appdata +{ + Evas_Object *window; + Evas_Object *conform; + Evas_Object *navi; + Evas_Object *msg_box; +} appdata_s; + +typedef struct privileges +{ + char* privileges[20]; +} privileges_s; + +typedef struct other_privileges +{ + char* privileges[6]; +} other_privileges_s; + +const privileges_s unique_privacies = { + { + "http://tizen.org/privilege/apphistory.read", + "http://tizen.org/privilege/audiorecorder", + "http://tizen.org/privilege/bookmark.read", + "http://tizen.org/privilege/bookmark.write", + "http://tizen.org/privilege/calendar.read", + "http://tizen.org/privilege/calendar.write", + "http://tizen.org/privilege/call", + "http://tizen.org/privilege/callhistory.read", + "http://tizen.org/privilege/callhistory.write", + "http://tizen.org/privilege/camera", + "http://tizen.org/privilege/externalstorage", + "http://tizen.org/privilege/healthinfo", + "http://tizen.org/privilege/location", + "http://tizen.org/privilege/messaging.read", + "http://tizen.org/privilege/messaging.write", + "http://tizen.org/privilege/mediacapture", + "http://tizen.org/privilege/mediastorage", + "http://tizen.org/privilege/push", + "http://tizen.org/privilege/power", + "http://tizen.org/privilege/recorder", + } +}; + +const other_privileges_s privacies_for_request_in_response_callback = { + { + "http://tizen.org/privilege/alarm.get", + "http://tizen.org/privilege/alarm.set", + "http://tizen.org/privilege/account.read", + "http://tizen.org/privilege/account.write", + "http://tizen.org/privilege/contact.read", + "http://tizen.org/privilege/contact.write", + } + +}; + +static void win_delete_request_cb(void *data, Evas_Object *obj, void *event_info); + +/** + * @brief Prints massage into the message box. + * + * @param[in] obj The message box. + * @param[in] msg The text message that is written to the message box. + * @param[in] ... Additional arguments declared in the text message. + */ +void print_msg(Evas_Object *obj, const char* msg, ...); + +/** + * @brief Clears the message box. + * + * @param[in] data User specific data. + * @param[in] btn Pointer to the button object. + * @param[in] event_info Additional event information. + */ +static void _btn_clear_cb(void *data, Evas_Object *btn, void *event_info); + +/** + * @brief Creates the display object. + */ +Evas_Object *_create_new_cd_display(appdata_s *ad, const char *name, void *cb); + +Eina_Bool _pop_cb(void *data, Elm_Object_Item *item); + +/** + * @brief Function copied from the Account Manager example. + */ +Evas_Object *_new_button(appdata_s *ad, Evas_Object *display, const char *name, void *cb); + +/** + * @brief Writes text into dlogutil and into GUI. + * + * @details In order to read a message using dlogutil, + * type in the emulator's terminal: dlogutil ppmtester + * The 'ppmtester' flag is set using LOG_TAG constant. + * + * @param[in] ad The structure with GUI elements. + * @param[in] privilege A privilege that is to be logged. + * @param[in] msg A text that is to be written. + */ +void log_privilege(appdata_s *ad, const char* privilege, const char* msg); + +/** + * @brief Logs result from ppm_check_pemission(s) callback + * + * @details In order to read a message using dlogutil, + * type in the emulator's terminal: dlogutil ppmtester + * The 'ppmtester' flag is set using LOG_TAG constant. + * + * @param[in] ad The structure with GUI elements. + * @param[in] result The result of request. + * @param[in] privilege A privilege that is to be logged. + */ +void log_result(appdata_s *ad, ppm_request_result_e result, const char *privilege); + +/** + * @brief Based on ppm_popup_response_cb prototype from privacy-privilege-manager.h. + * + * @param[in] cause A value representing the reason why this callback + * has been called. + * @param[in] result A result of a response triggered by calling ppm_popup_request(). + * @param[in] privilege A privilege that has been checked. + * @param[in] user_data User specific data. + */ +void ppm_popup_response_cb_function(ppm_call_cause_e cause, ppm_request_result_e result, const char *privilege, void *user_data); + +/** + * @brief Based on ppm_request_multiple_response_cb prototype from privacy-privilege-manager.h. + * + * @param[in] cause A value representing the reason why this callback + * has been called. + * @param[in] results A results of a responses triggered by calling ppm_popup_requests(). + * @param[in] privileges A privileges that has been checked. + * @param[in] privilege_count The number of elements in the privileges and results array + * @param[in] user_data User specific data. + */ +void ppm_popup_multiple_response_cb_function (ppm_call_cause_e cause, const ppm_request_result_e *results, const char **privileges, + size_t privileges_count, void *user_data); + +/** + * @brief Checks the privilege. Writes messages in the bottom part of the main window and + * into the shell (in order to read these messages use command: dlogutil ppmtester). + * + * @param[in] ad The structure with GUI elements. + * @param[in] privilege A privilege that is to be checked. + * @param[in] ret The code of the error. + */ +void app_log_errors(appdata_s *ad, const char* privilege, ppm_error_e ret); + +/** + * @brief Checks the privilege. Writes messages in the bottom part of the main window and + * into the shell (in order to read these messages use command: dlogutil ppmtester). + * + * @param[in] ad The structure with GUI elements. + * @param[in] privilege A privilege that is to be checked. + */ +void check_privilege(appdata_s *ad, const char* privilege); + +/** + * @brief Checks the privileges. Writes messages in the bottom part of the main window and + * into the shell (in order to read these messages use command: dlogutil ppmtester). + * + * @param[in] ad The structure with GUI elements. + * @param[in] privileges A privileges that is to be checked. + * @param[in] privileges_count Privileges count passed as second argument + * @param[in] request_privs_no_ask_status If this flag is set to true + */ +void check_privileges(appdata_s *ad, const char** privileges, size_t privileges_count, bool request_privs_no_ask_status); + +/** + * @brief Callback for 'camera' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_privilege_camera(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Callback for 'location' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_privilege_location(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Callback for 'contact.read' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_privilege_contact_read(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Callback for 'contact.write' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_privilege_contact_write(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Callback for 'check.privacies' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_check_privacy_privileges(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Callback for 'check.privacies.ignore.ask' button. + * + * @param[in] ad The structure with GUI elements. + * @param[in] obj Display object. + */ +void _test_check_privacy_privileges_ignore_ask(appdata_s *ad, Evas_Object *obj); + +/** + * @brief Creates buttons in the main window. + * + * @param[in] ad The structure with GUI elements. + */ +void create_buttons_in_main_window(appdata_s *ad); + +/** + * @brief Creates base GUI (window, conformant, naviframe and buttons). + * + * @param[in] ad The structure with GUI elements. + */ +static void create_base_gui(appdata_s *ad); + +/** + * @brief Initializes ui resources and application`s data before the main loop starts. + * + * @return true on success, false if error occured + */ +static bool app_create(void *data); + +/** + * @brief Handles the launch request + * @param[in] app_control The application control handle. + * @param[in] data Application data. + */ +static void app_control(app_control_h app_control, void *data); + +/** + * @brief Handles application pause event. + * @param[in] data Application data. + */ +static void app_pause(void *data); + +/** + * @brief Handles application resume event. + * @param[in] data Application data. + */ +static void app_resume(void *data); + +/** + * @brief Handles application termination event. + * @param[in] data Application data. + */ +static void app_terminate(void *data); + +int main(int argc, char *argv[]); + +#endif /* PPMTESTER_H_ */