pebble/tests/fw/services/test_phone_call.c
2025-01-27 11:38:16 -08:00

285 lines
9.6 KiB
C

/*
* Copyright 2024 Google LLC
*
* 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.
*/
#include "clar.h"
#include "kernel/events.h"
#include "services/common/comm_session/session.h"
#include "services/normal/notifications/alerts.h"
#include "services/normal/phone_call.h"
#include "services/normal/phone_call_util.h"
extern T_STATIC void prv_handle_phone_event(PebbleEvent *e, void *context);
extern T_STATIC void prv_handle_mobile_app_event(PebbleEvent *e, void *context);
extern T_STATIC void prv_handle_ancs_disconnected_event(PebbleEvent *e, void *context);
///////////////////////////////////////////////////////////
// Stubs
///////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_event_service_client.h"
#include "stubs_logging.h"
#include "stubs_new_timer.h"
#include "stubs_pbl_malloc.h"
#include "stubs_phone_call_util.h"
#include "stubs_session.h"
#include "stubs_system_task.h"
bool alerts_should_notify_for_type(AlertType type) {
return true;
}
void ancs_perform_action(uint32_t notification_uid, uint8_t action_id) {}
void ancs_phone_call_temporarily_block_missed_calls(void) {}
void pp_answer_call(uint32_t cookie) {}
void pp_decline_call(uint32_t cookie) {}
void pp_get_phone_state(void) {}
void pp_get_phone_state_set_enabled(bool enabled) {}
// Phone UI stubs that allow us to track what phone_call.c is doing
static PhoneEventType s_last_phone_ui_event;
void phone_ui_handle_incoming_call(PebblePhoneCaller *caller, bool can_answer,
bool show_ongoing_call_ui) {
s_last_phone_ui_event = PhoneEventType_Incoming;
}
void phone_ui_handle_outgoing_call(PebblePhoneCaller *caller) {
s_last_phone_ui_event = PhoneEventType_Outgoing;
}
void phone_ui_handle_missed_call(void) {
s_last_phone_ui_event = PhoneEventType_Missed;
}
void phone_ui_handle_call_start(bool can_decline) {
s_last_phone_ui_event = PhoneEventType_Start;
}
void phone_ui_handle_call_end(bool call_accepted, bool disconnected) {
s_last_phone_ui_event = PhoneEventType_End;
}
void phone_ui_handle_call_hide(void) {
s_last_phone_ui_event = PhoneEventType_Hide;
}
void phone_ui_handle_caller_id(PebblePhoneCaller *caller) {
s_last_phone_ui_event = PhoneEventType_CallerID;
}
///////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////
#define ANCS_CALL_UID 1
#define ANCS_UNUSED_UID 2
// Whenever we check the last phone ui event, we reset s_last_phone_ui_event so we don't end up
// checking the same event twice and assume everything went well
#define ASSERT_LAST_EVENT(event) \
cl_assert_equal_i(s_last_phone_ui_event, event); \
s_last_phone_ui_event = PhoneEventType_Invalid;
static void prv_put_comm_session_event(bool app_connected) {
PebbleEvent comm_session_event = {
.type = PEBBLE_COMM_SESSION_EVENT,
.bluetooth.comm_session_event = (PebbleCommSessionEvent) {
.is_system = true,
.is_open = app_connected,
}
};
prv_handle_mobile_app_event(&comm_session_event, NULL);
}
static void prv_put_phone_event(PhoneEventType type, PhoneCallSource source,
uint32_t call_identifier) {
PebbleEvent phone_event = {
.type = PEBBLE_PHONE_EVENT,
.phone = {
.type = type,
.source = source,
.call_identifier = call_identifier,
.caller = NULL,
}
};
prv_handle_phone_event(&phone_event, NULL);
}
static void prv_put_incoming_call_event(PhoneCallSource source, bool app_connected) {
prv_put_comm_session_event(app_connected);
prv_put_phone_event(PhoneEventType_Incoming, source, ANCS_CALL_UID);
}
static void prv_call_end(void) {
// Note: the source doesn't matter here, phone_call.c ignores it
prv_put_phone_event(PhoneEventType_End, PhoneCallSource_PP, ANCS_CALL_UID);
}
static void prv_call_start(void) {
// Note: the source doesn't matter here, phone_call.c ignores it
prv_put_phone_event(PhoneEventType_Start, PhoneCallSource_PP, ANCS_CALL_UID);
}
static void prv_call_hide(uint32_t call_identifier) {
// Note: the source doesn't matter here, phone_call.c ignores it
prv_put_phone_event(PhoneEventType_Hide, PhoneCallSource_PP, call_identifier);
}
static void prv_ancs_disconnect(void) {
PebbleEvent ancs_event = {
.type = PEBBLE_ANCS_DISCONNECTED_EVENT,
};
prv_handle_ancs_disconnected_event(&ancs_event, NULL);
}
///////////////////////////////////////////////////////////
// Tests
///////////////////////////////////////////////////////////
void test_phone_call__initialize(void) {
//fake_comm_session_init();
phone_call_service_init();
prv_call_end();
s_last_phone_ui_event = PhoneEventType_Invalid;
// s_transport = fake_transport_create(TransportDestinationSystem, NULL, NULL);
// s_session = fake_transport_set_connected(s_transport, true /* connected */);
// pp_get_phone_state_set_enabled(false);
}
// ---------------------------------------------------------------------------------------
void test_phone_call__cleanup(void) {
// fake_comm_session_cleanup();
}
// ---------------------------------------------------------------------------------------
// Basic test for incoming calls over PP
void test_phone_call__pp_incoming(void) {
// We should only allow incoming calls when connected to the mobile app (this should never really
// happen for PP)
prv_put_incoming_call_event(PhoneCallSource_PP, false);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
prv_put_incoming_call_event(PhoneCallSource_PP, true);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
// Make sure we don't process incoming calls while we're in a call
prv_put_incoming_call_event(PhoneCallSource_PP, true);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing ANCS connectivity in this case shouldn't matter
prv_ancs_disconnect();
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing mobile connection should end the call
prv_put_comm_session_event(false /* app_connected */);
ASSERT_LAST_EVENT(PhoneEventType_End);
}
// ---------------------------------------------------------------------------------------
// Basic test for incoming calls over ANCS on iOS 8 and below
void test_phone_call__ancs_legacy_incoming(void) {
// We should only allow the incoming call if we're connected to the app for polling reasons
prv_put_incoming_call_event(PhoneCallSource_ANCS_Legacy, false);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
prv_put_incoming_call_event(PhoneCallSource_ANCS_Legacy, true);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
// Make sure we don't process incoming calls while we're in a call
prv_put_incoming_call_event(PhoneCallSource_ANCS_Legacy, true);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing ANCS connectivity in this case shouldn't matter
prv_ancs_disconnect();
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing mobile app connection should end the call on the watch
prv_put_comm_session_event(false /* app_connected */);
ASSERT_LAST_EVENT(PhoneEventType_End);
}
// ---------------------------------------------------------------------------------------
// Basic test for incoming calls on iOS 9 and up
void test_phone_call__ancs_incoming(void) {
// We should allow incoming calls with or without a mobile app on iOS 9
prv_put_incoming_call_event(PhoneCallSource_ANCS, false);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
prv_call_end();
ASSERT_LAST_EVENT(PhoneEventType_End);
prv_put_incoming_call_event(PhoneCallSource_ANCS, true);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
// Make sure we don't process incoming calls while we're in a call
prv_put_incoming_call_event(PhoneCallSource_ANCS, true);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing connection to mobile app should have no effect if iOS 9
prv_put_comm_session_event(false /* app_connected */);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
// Losing ANCS here should end the call
prv_ancs_disconnect();
ASSERT_LAST_EVENT(PhoneEventType_End);
}
// ---------------------------------------------------------------------------------------
// Basic test for call start events
void test_phone_call__call_start(void) {
// A call start event with ANCS should act as a call end in order to hide the phone ui
prv_put_incoming_call_event(PhoneCallSource_ANCS_Legacy, true);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
prv_call_start();
ASSERT_LAST_EVENT(PhoneEventType_End);
// A call start event with PP should keep the phone ui up
prv_put_incoming_call_event(PhoneCallSource_PP, true);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
prv_call_start();
ASSERT_LAST_EVENT(PhoneEventType_Start);
}
// ---------------------------------------------------------------------------------------
// Make sure we handle ANCS notification removals properly
void test_phone_call__ancs_hide(void) {
// Make sure we hide the call when ANCS tells us the notification was removed (but only if the
// call id matches the current call)
prv_put_incoming_call_event(PhoneCallSource_ANCS, false);
ASSERT_LAST_EVENT(PhoneEventType_Incoming);
prv_call_hide(ANCS_UNUSED_UID);
ASSERT_LAST_EVENT(PhoneEventType_Invalid);
prv_call_hide(ANCS_CALL_UID);
ASSERT_LAST_EVENT(PhoneEventType_Hide);
}