From: Pawel Kowalski Date: Tue, 25 Jul 2017 11:34:23 +0000 (+0200) Subject: Add new application for testing the CAPI. X-Git-Tag: accepted/tizen/4.0/unified/20170920.081734~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d1f6e45e2e9687754444cd46078767e65d5fb0a8;p=platform%2Fcore%2Fsecurity%2Faskuser.git Add new application for testing the CAPI. The app is written in Tizen Studio 4.0 and is based on the example 'Account Manager' for profile mobile-4.0. Change-Id: Id05dda75cde1a20e80d7a7743f3df8b3e148e571 --- diff --git a/test/apps/capi/PPMTester/.cproject b/test/apps/capi/PPMTester/.cproject new file mode 100644 index 0000000..36a2f92 --- /dev/null +++ b/test/apps/capi/PPMTester/.cproject @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/apps/capi/PPMTester/.exportMap b/test/apps/capi/PPMTester/.exportMap new file mode 100644 index 0000000..de30516 --- /dev/null +++ b/test/apps/capi/PPMTester/.exportMap @@ -0,0 +1,5 @@ +{ + global: main; + _IO_*; + local: *; +}; diff --git a/test/apps/capi/PPMTester/.project b/test/apps/capi/PPMTester/.project new file mode 100644 index 0000000..160aa91 --- /dev/null +++ b/test/apps/capi/PPMTester/.project @@ -0,0 +1,26 @@ + + + PPMTester + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/test/apps/capi/PPMTester/.tproject b/test/apps/capi/PPMTester/.tproject new file mode 100644 index 0000000..468a834 --- /dev/null +++ b/test/apps/capi/PPMTester/.tproject @@ -0,0 +1,12 @@ + + + + + mobile-4.0 + + + + + + + diff --git a/test/apps/capi/PPMTester/README b/test/apps/capi/PPMTester/README new file mode 100644 index 0000000..9aedf43 --- /dev/null +++ b/test/apps/capi/PPMTester/README @@ -0,0 +1,45 @@ +README for PPMTester app + +The PPMTester app is an app created for testing CAPI of Privacy Privilege +Manager. The app is created in Tizen Studio 4.0 and is based on the +example app 'Account Manager' for profile mobile-4.0. + +In order to import the app into the Tizen Studio and build it for Tizen +emulator, one needs to perform following steps (steps 1. and 2. may be +ommited if the CAPI is already installed in Tizen Studio IDE and Tizen +Emulator; if mobile-4 profile may be accessed when new a project is +created, then probably the CAPI is installed): + + 1 Prepare Tizen Studio IDE + a) Download askuser from gerrit repository and build it using gbs + b) Unpack generated rpm files + c) Copy libraries and includes into relevant directories in + $HOME/tizen-studio/platforms/tizen-4.0/mobile/rootstraps/ + /mobile-4.0-emulator.core/usr/ + d) Modify XML file + $HOME/tizen-studio/platforms/tizen-4.0/mobile/rootstraps/ + /info/mobile-4.0-emulator.core.dev.xml + and add following lines in proper places: + /usr/include/privacy-privilege-manager + libcapi-privacy-privilege-manager.so + e) Remove library dependencies for each .so file extracted from + rpm in step b) *separately*. This can be done using tool + 'postlink' from native-app-rootstrap project repository. + Usage: /path/to/postlink /path/to/libX.so + The postlink command may be executed from any directory + (absolute paths to postlink and .so files may be used). + + 2. Prepare Tizen Emulator + a) Run Tizen 4.0 Emulator (x86) from Tizen Studio + b) Copy askuser rpms using sdb: sdb push *.rpm /tmp + c) Connect to the emulator and install packages: + sdb root on; sdb shell; mount -o remount,rw /; cd /tmp; + rpm -i *.rpm --force --nodeps + + 3. Import and build project in Tizen Studio + a) File -> Import... -> General, Existing Projects into Workspace -> + -> Next -> Select root directory using 'Browse' button -> + -> Check in 'Copy projects into workspace' -> Finish + b) Select proper emulator from drop-down list + c) Project -> Build All + d) Run diff --git a/test/apps/capi/PPMTester/project_def.prop b/test/apps/capi/PPMTester/project_def.prop new file mode 100644 index 0000000..f923439 --- /dev/null +++ b/test/apps/capi/PPMTester/project_def.prop @@ -0,0 +1,11 @@ +APPNAME = ppmtester + +type = app +profile = mobile-4.0 + +USER_SRCS = src/ppmtester.c +USER_DEFS = +USER_INC_DIRS = +USER_OBJS = +USER_LIBS = +USER_EDCS = diff --git a/test/apps/capi/PPMTester/shared/res/ppmtester.png b/test/apps/capi/PPMTester/shared/res/ppmtester.png new file mode 100644 index 0000000..9765b1b Binary files /dev/null and b/test/apps/capi/PPMTester/shared/res/ppmtester.png differ diff --git a/test/apps/capi/PPMTester/src/ppmtester.c b/test/apps/capi/PPMTester/src/ppmtester.c new file mode 100644 index 0000000..efbc62c --- /dev/null +++ b/test/apps/capi/PPMTester/src/ppmtester.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2017 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.c + * @author Pawel Kowalski + * @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; + +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; + va_start(args, msg); + char buffer[BUFFER_SIZE]; + vsnprintf(buffer, BUFFER_SIZE, msg, args); + va_end(args); + + Evas_Coord c_y; + elm_entry_entry_append(obj, buffer); + elm_entry_entry_append(obj, "
"); + elm_entry_cursor_end_set(obj); + elm_entry_cursor_geometry_get(obj, NULL, &c_y, NULL, NULL); + 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 + Evas_Object *scroller = elm_scroller_add(ad->window); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + + // Create a new item + Elm_Object_Item *item = + elm_naviframe_item_push(ad->navi, "PPM Tester", NULL, NULL, scroller, NULL); + elm_object_item_part_text_set(item, "subtitle", "Test following privileges:"); + + if (cb != NULL) + elm_naviframe_item_pop_cb_set(item, (Elm_Naviframe_Item_Pop_Cb) cb, (void *)ad); + + // Create a main box + Evas_Object *box = elm_box_add(scroller); + elm_object_content_set(scroller, box); + elm_box_horizontal_set(box, EINA_FALSE); + evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(box); + + Evas_Object *inner_grid = elm_grid_add(box); + elm_object_content_set(box, inner_grid); + evas_object_size_hint_align_set(inner_grid, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(inner_grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(inner_grid); + + Evas_Object *bbox_scroller = elm_scroller_add(inner_grid); + evas_object_size_hint_align_set(bbox_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bbox_scroller, EVAS_HINT_EXPAND, 0.0); + elm_scroller_policy_set(bbox_scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON); + elm_object_style_set(bbox_scroller, "handler"); + evas_object_show(bbox_scroller); + + // Create a box for adding content + Evas_Object *bbox = elm_box_add(bbox_scroller); + Evas_Coord padding_between_buttons = 3; + elm_box_padding_set(bbox, 0, padding_between_buttons); + elm_object_content_set(bbox_scroller, bbox); + elm_box_horizontal_set(bbox, EINA_FALSE); + evas_object_size_hint_align_set(bbox, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bbox, EVAS_HINT_EXPAND, 0.0); + evas_object_show(bbox); + + // Create "Clear" button + Evas_Object *bt = elm_button_add(box); + elm_object_text_set(bt, "Clear"); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + evas_object_smart_callback_add(bt, "clicked", _btn_clear_cb, ad); + evas_object_show(bt); + + // Create a box for entry + Evas_Object *ebox = elm_box_add(inner_grid); + elm_box_horizontal_set(ebox, EINA_FALSE); + evas_object_size_hint_align_set(ebox, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(ebox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(ebox); + + // Create a message box + Evas_Object *display_window = elm_entry_add(ebox); + evas_object_size_hint_align_set(display_window, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(display_window, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(display_window); + + elm_entry_editable_set(display_window, EINA_FALSE); + elm_entry_scrollable_set(display_window, EINA_TRUE); + + ad->msg_box = display_window; + elm_scroller_policy_set(ad->msg_box, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON); + elm_box_pack_end(ebox, display_window); + + elm_grid_pack(inner_grid, bbox_scroller, 0, 0, 100, 35); + elm_grid_pack(inner_grid, ebox, 0, 35, 100, 65); + elm_box_pack_end(box, inner_grid); + elm_box_pack_end(box, bt); + + return bbox; +} + +Eina_Bool _pop_cb(void *data, Elm_Object_Item *item) +{ + elm_win_lower(((appdata_s *)data)->window); + 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 + Evas_Object *bt = elm_button_add(display); + elm_object_text_set(bt, name); + evas_object_smart_callback_add(bt, "clicked", (Evas_Smart_Cb) cb, ad); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_pack_end(display, bt); + evas_object_show(bt); + 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) +{ + print_msg(ad->msg_box, "----------
Privilege: %s
Message: %s", privilege, msg); + dlog_print(DLOG_INFO, LOG_TAG, "----------\nPrivilege: %s\nMessage: %s", privilege, 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) +{ + 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; + } + break; + case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR: + log_privilege(ad, privilege, "Callback was called because of an error."); + break; + default: + { + char msg[BUFFER_SIZE]; + snprintf(msg, BUFFER_SIZE, "Unknown cause: %d", cause); + log_privilege(ad, privilege, msg); + break; + } + } +} + +/** + * @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) +{ + switch (ret) + { + case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR: + log_privilege(ad, privilege, "I/O error"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER: + log_privilege(ad, privilege, "Invalid parameter"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS: + log_privilege(ad, privilege, "Operation already in progress"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY: + log_privilege(ad, privilege, "Out of memory"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN: + log_privilege(ad, privilege, "Unknown error"); + break; + default: + log_privilege(ad, privilege, "Unknown error"); + break; + } +} + +/** + * @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) +{ + ppm_check_result_e result; + + int ret = ppm_check_permission(privilege, &result); + + if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) // If there is an error, print log. + { + app_log_errors(ad, privilege, ret); + return; + } + + switch (result) // Alow, deny or ask. + { + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW: + log_privilege(ad, privilege, "An application has permission to use a privilege."); + break; + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY: + log_privilege(ad, privilege, "An application doesn't have permission to use a privilege."); + break; + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK: + { + log_privilege(ad, privilege, "A user has to be asked whether to grant permission to use a privilege."); + int ret_popup = ppm_request_permission(privilege, ppm_popup_response_cb_function, ad); + if (ret_popup != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) + { + app_log_errors(ad, privilege, ret_popup); + return; + } + break; + } + default: + { + char msg[BUFFER_SIZE]; + snprintf(msg, BUFFER_SIZE, "Unknown result: %d", result); + log_privilege(ad, privilege, msg); + break; + } + } +} + +/** + * @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) +{ + 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 create_buttons_in_main_window(appdata_s *ad) +{ + Evas_Object *display = _create_new_cd_display(ad, "PPM Tester", _pop_cb); + + _new_button(ad, display, "camera", _test_privilege_camera); + _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); +} + +/** + * @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 + ad->window = elm_win_util_standard_add(PACKAGE, PACKAGE); + elm_win_conformant_set(ad->window, EINA_TRUE); + elm_win_autodel_set(ad->window, EINA_TRUE); + elm_win_indicator_mode_set(ad->window, ELM_WIN_INDICATOR_SHOW); + elm_win_indicator_opacity_set(ad->window, ELM_WIN_INDICATOR_OPAQUE); + evas_object_smart_callback_add(ad->window, "delete, request", win_delete_request_cb, NULL); + + // Create conformant + ad->conform = elm_conformant_add(ad->window); + evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_win_resize_object_add(ad->window, ad->conform); + evas_object_show(ad->conform); + + // Create a naviframe + ad->navi = elm_naviframe_add(ad->conform); + evas_object_size_hint_align_set(ad->navi, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(ad->navi, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + + elm_object_content_set(ad->conform, ad->navi); + evas_object_show(ad->navi); + + // Fill the list with items + create_buttons_in_main_window(ad); + + eext_object_event_callback_add(ad->navi, EEXT_CALLBACK_BACK, eext_naviframe_back_cb, NULL); + + // Show the window after base gui is set up + evas_object_show(ad->window); +} + +static bool app_create(void *data) +{ + /* + * Hook to take necessary actions before main event loop starts + * Initialize UI resources and application's data + * If this function returns true, the main loop of application starts + * If this function returns false, the application is terminated + */ + + appdata_s *ad = data; + + create_base_gui(ad); + + return true; +} + +static void app_control(app_control_h app_control, void *data) +{ + // Handle the launch request. +} + +static void app_pause(void *data) +{ + // Take necessary actions when application becomes invisible. +} + +static void app_resume(void *data) +{ + // Take necessary actions when application becomes visible. + + // Checking two example privileges on resume: + const char* privileges [] = {"http://tizen.org/privilege/alarm.get", "http://tizen.org/privilege/calendar.read"}; + for (int i = 0; i < sizeof(privileges)/sizeof(privileges[0]); i++) + { + check_privilege(data, privileges[i]); + } +} + +static void app_terminate(void *data) +{ + // Release all resources. +} + +int main(int argc, char *argv[]) +{ + appdata_s ad = {0,}; + int ret = 0; + + ui_app_lifecycle_callback_s event_callback = {0,}; + + event_callback.create = app_create; + event_callback.terminate = app_terminate; + event_callback.pause = app_pause; + event_callback.resume = app_resume; + event_callback.app_control = app_control; + + ret = ui_app_main(argc, argv, &event_callback, &ad); + if (ret != APP_ERROR_NONE) + { + dlog_print(DLOG_ERROR, LOG_TAG, "app_main() is failed. err = %d", ret); + } + + return ret; +} diff --git a/test/apps/capi/PPMTester/tizen-manifest.xml b/test/apps/capi/PPMTester/tizen-manifest.xml new file mode 100644 index 0000000..344e47e --- /dev/null +++ b/test/apps/capi/PPMTester/tizen-manifest.xml @@ -0,0 +1,17 @@ + + + + + + ppmtester.png + + + http://tizen.org/privilege/calendar.write + http://tizen.org/privilege/contact.read + http://tizen.org/privilege/contact.write + http://tizen.org/privilege/location + http://tizen.org/privilege/calendar.read + http://tizen.org/privilege/alarm.get + http://tizen.org/privilege/alarm.set + + \ No newline at end of file