| // Copyright 2021 The Cobalt Authors. 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. |
| |
| #include <asm/types.h> |
| #include <errno.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <memory.h> |
| #include <stdio.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| #include <cstring> |
| |
| #include "starboard/common/log.h" |
| #include "starboard/shared/linux/singleton.h" |
| #include "starboard/shared/linux/system_network_status.h" |
| #include "starboard/shared/pthread/thread_create_priority.h" |
| #include "starboard/shared/starboard/application.h" |
| #include "starboard/system.h" |
| |
| namespace { |
| |
| // This function will repeatedly run on the NetworkNotifier thread untile the |
| // Cobalt application is kill. This function checks kernel message for IP |
| // address changes. |
| bool GetOnlineStatus(bool* is_online_ptr, int netlink_fd) { |
| SB_DCHECK(is_online_ptr != NULL); |
| |
| struct sockaddr_nl sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.nl_family = AF_NETLINK; |
| sa.nl_groups = RTMGRP_IPV4_IFADDR; |
| sa.nl_pid = getpid(); |
| int bind_result = bind(netlink_fd, (struct sockaddr*)&sa, sizeof(sa)); |
| SB_DCHECK(bind_result == 0); |
| |
| char buf[8192]; |
| struct iovec iov; |
| iov.iov_base = buf; |
| iov.iov_len = sizeof(buf); |
| |
| struct msghdr msg; |
| { |
| msg.msg_name = &sa; |
| msg.msg_namelen = sizeof(sa); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| } |
| |
| ssize_t status; |
| status = recvmsg(netlink_fd, &msg, MSG_DONTWAIT); |
| bool has_message = false; |
| while (status >= 0) { |
| SB_DCHECK(msg.msg_namelen == sizeof(sa)); |
| |
| struct nlmsghdr* header; |
| |
| for (header = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*header);) { |
| int len = header->nlmsg_len; |
| int l = len - sizeof(*header); |
| |
| SB_DCHECK(l >= 0); |
| SB_DCHECK(len <= status); |
| |
| switch (header->nlmsg_type) { |
| case RTM_DELADDR: |
| *is_online_ptr = false; |
| has_message = true; |
| break; |
| case RTM_NEWADDR: |
| *is_online_ptr = true; |
| has_message = true; |
| break; |
| } |
| |
| status -= NLMSG_ALIGN(len); |
| header = (struct nlmsghdr*)(reinterpret_cast<char*>(header) + |
| NLMSG_ALIGN(len)); |
| } |
| status = recvmsg(netlink_fd, &msg, MSG_DONTWAIT); |
| } |
| |
| return has_message; |
| } |
| |
| } // namespace |
| |
| bool NetworkNotifier::Initialize() { |
| SB_DCHECK(notifier_thread_ == 0); |
| |
| pthread_attr_t attributes; |
| int result = pthread_attr_init(&attributes); |
| if (result != 0) { |
| return false; |
| } |
| |
| pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); |
| pthread_create(¬ifier_thread_, &attributes, |
| &NetworkNotifier::NotifierThreadEntry, this); |
| pthread_attr_destroy(&attributes); |
| |
| SB_DCHECK(notifier_thread_ != 0); |
| return true; |
| } |
| |
| void* NetworkNotifier::NotifierThreadEntry(void* context) { |
| pthread_setname_np(pthread_self(), "NetworkNotifier"); |
| starboard::shared::pthread::ThreadSetPriority(kSbThreadPriorityLow); |
| auto* notifier = static_cast<NetworkNotifier*>(context); |
| int netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| bool is_online; |
| do { |
| if (GetOnlineStatus(&is_online, netlink_fd)) { |
| notifier->set_online(is_online); |
| auto* application = starboard::shared::starboard::Application::Get(); |
| if (is_online) { |
| application->InjectOsNetworkConnectedEvent(); |
| } else { |
| application->InjectOsNetworkDisconnectedEvent(); |
| } |
| } |
| usleep(1000); |
| } while (1); |
| |
| return nullptr; |
| } |
| |
| bool SbSystemNetworkIsDisconnected() { |
| return !NetworkNotifier::GetOrCreateInstance()->is_online(); |
| } |