| /* |
| * Copyright (C) 2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "EndowmentStateTracker.h" |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| #import "Logging.h" |
| #import "RunningBoardServicesSPI.h" |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/RunLoop.h> |
| |
| namespace WebKit { |
| |
| static NSString* visibilityEndowment = @"com.apple.frontboard.visibility"; |
| static NSString* userfacingEndowment = @"com.apple.launchservices.userfacing"; |
| |
| static RBSProcessHandle *handleForPID(pid_t pid) |
| { |
| RBSProcessIdentifier *processIdentifier = [RBSProcessIdentifier identifierWithPid:pid]; |
| if (!processIdentifier) { |
| RELEASE_LOG_ERROR(ProcessSuspension, "handleForPID: Failed to construct RBSProcessIdentifier from PID %d", pid); |
| return nil; |
| } |
| |
| NSError *error = nil; |
| RBSProcessHandle *processHandle = [RBSProcessHandle handleForIdentifier:processIdentifier error:&error]; |
| if (!processHandle) { |
| RELEASE_LOG_ERROR(ProcessSuspension, "endowmentsForPid: Failed to get RBSProcessHandle for process with PID %d, error: %{public}@", pid, error); |
| return nil; |
| } |
| |
| return processHandle; |
| } |
| |
| static NSSet<NSString *> *endowmentsForHandle(RBSProcessHandle *processHandle) |
| { |
| if (!processHandle) { |
| // We assume foreground when unable to determine state to maintain pre-existing behavior and to avoid |
| // not rendering anything when we fail. |
| return [NSSet setWithObjects:visibilityEndowment, userfacingEndowment, nil]; |
| } |
| |
| RBSProcessState *state = processHandle.currentState; |
| if (state.taskState != RBSTaskStateRunningScheduled) { |
| RELEASE_LOG_ERROR(ProcessSuspension, "endowmentsForHandle: Process with PID %d is not running", processHandle.pid); |
| return nil; |
| } |
| |
| return [state endowmentNamespaces]; |
| } |
| |
| inline auto EndowmentStateTracker::stateFromEndowments(NSSet *endowments) -> State |
| { |
| return State { |
| !![endowments containsObject:userfacingEndowment], |
| !![endowments containsObject:visibilityEndowment] |
| }; |
| } |
| |
| bool EndowmentStateTracker::isApplicationForeground(pid_t pid) |
| { |
| return [endowmentsForHandle(handleForPID(pid)) containsObject:visibilityEndowment]; |
| } |
| |
| EndowmentStateTracker& EndowmentStateTracker::singleton() |
| { |
| static auto tracker = NeverDestroyed<EndowmentStateTracker>(); |
| return tracker; |
| } |
| |
| void EndowmentStateTracker::registerMonitorIfNecessary() |
| { |
| if (m_processMonitor) |
| return; |
| |
| m_processMonitor = [RBSProcessMonitor monitorWithConfiguration:[this] (id<RBSProcessMonitorConfiguring> config) { |
| [config setPredicates:@[[RBSProcessPredicate predicateMatchingHandle:[RBSProcessHandle currentProcess]]]]; |
| |
| RBSProcessStateDescriptor *stateDescriptor = [RBSProcessStateDescriptor descriptor]; |
| stateDescriptor.endowmentNamespaces = @[visibilityEndowment, userfacingEndowment]; |
| [config setStateDescriptor:stateDescriptor]; |
| |
| [config setUpdateHandler:[this] (RBSProcessMonitor * _Nonnull monitor, RBSProcessHandle * _Nonnull process, RBSProcessStateUpdate * _Nonnull update) mutable { |
| RunLoop::main().dispatch([this, state = stateFromEndowments(update.state.endowmentNamespaces)]() mutable { |
| setState(WTFMove(state)); |
| }); |
| }]; |
| }]; |
| } |
| |
| void EndowmentStateTracker::addClient(Client& client) |
| { |
| m_clients.add(client); |
| registerMonitorIfNecessary(); |
| } |
| |
| void EndowmentStateTracker::removeClient(Client& client) |
| { |
| m_clients.remove(client); |
| } |
| |
| auto EndowmentStateTracker::ensureState() const -> const State& |
| { |
| if (!m_state) |
| m_state = stateFromEndowments(endowmentsForHandle([RBSProcessHandle currentProcess])); |
| return *m_state; |
| } |
| |
| void EndowmentStateTracker::setState(State&& state) |
| { |
| bool isUserFacingChanged = !m_state || m_state->isUserFacing != state.isUserFacing; |
| bool isVisibleChanged = !m_state || m_state->isVisible != state.isVisible; |
| if (!isUserFacingChanged && !isVisibleChanged) |
| return; |
| |
| m_state = WTFMove(state); |
| |
| RELEASE_LOG(ViewState, "%p - EndowmentStateTracker::setState() isUserFacing: %{public}s isVisible: %{public}s", this, m_state->isUserFacing ? "true" : "false", m_state->isVisible ? "true" : "false"); |
| |
| for (auto& client : copyToVector(m_clients)) { |
| if (isUserFacingChanged && client) |
| client->isUserFacingChanged(m_state->isUserFacing); |
| if (isVisibleChanged && client) |
| client->isVisibleChanged(m_state->isVisible); |
| } |
| } |
| |
| } |
| |
| #endif // PLATFORM(IOS_FAMILY) |