Skip to content

Commit 370ce8d

Browse files
Add iOS background downloading harness to Context (#1447)
Context allows to inform iOS network implementation that the app is going to switch background-foreground. And it allows to store background session completion handlers. Relates-To: OLPEDGE-2847 Signed-off-by: Rustam Gamidov <ext-rustam.gamidov@here.com>
1 parent fc29348 commit 370ce8d

File tree

10 files changed

+467
-64
lines changed

10 files changed

+467
-64
lines changed

olp-cpp-sdk-core/CMakeLists.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,12 @@ set(OLP_SDK_HTTP_SOURCES
293293
./src/http/NetworkUtils.cpp
294294
)
295295

296+
set(OLP_SDK_PLATFORM_SOURCES
297+
./src/context/Context.cpp
298+
./src/context/ContextInternal.cpp
299+
./src/context/ContextInternal.h
300+
)
301+
296302
if (ANDROID)
297303
# http network Android implementation:
298304
set(OLP_SDK_HTTP_ANDROID_SOURCES
@@ -314,6 +320,8 @@ endif()
314320

315321
if (IOS)
316322
set(OLP_SDK_HTTP_SOURCES ${OLP_SDK_HTTP_SOURCES} ${OLP_SDK_HTTP_IOS_SOURCES})
323+
set(OLP_SDK_PLATFORM_HEADERS ${OLP_SDK_PLATFORM_HEADERS} ${OLP_SDK_PLATFORM_IOS_HEADERS})
324+
set(OLP_SDK_PLATFORM_SOURCES ${OLP_SDK_PLATFORM_SOURCES} ${OLP_SDK_PLATFORM_IOS_SOURCES})
317325

318326
set(OLP_SDK_DEFAULT_NETWORK_DEFINITION OLP_SDK_NETWORK_HAS_IOS)
319327
endif()
@@ -328,10 +336,6 @@ if (WIN32)
328336
endif()
329337
endif()
330338

331-
set(OLP_SDK_PLATFORM_SOURCES
332-
./src/context/Context.cpp
333-
)
334-
335339
set(OLP_SDK_UTILS_SOURCES
336340
./src/utils/Base64.cpp
337341
./src/utils/BoostExceptionHandle.cpp

olp-cpp-sdk-core/cmake/ios.cmake

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ if(IOS)
2828
"${CMAKE_CURRENT_LIST_DIR}/../src/http/ios/OLPHttpClient.h"
2929
)
3030

31+
set(OLP_SDK_PLATFORM_IOS_HEADERS
32+
"${CMAKE_CURRENT_LIST_DIR}/../include/olp/core/context/EnterBackgroundSubscriber.h"
33+
)
34+
set(OLP_SDK_PLATFORM_IOS_SOURCES
35+
"${CMAKE_CURRENT_LIST_DIR}/../src/context/ios/ContextInternal.mm"
36+
)
37+
3138
add_definitions(-DOLP_SDK_NETWORK_HAS_IOS)
3239
else()
40+
set(OLP_SDK_PLATFORM_IOS_HEADERS)
41+
set(OLP_SDK_PLATFORM_IOS_SOURCES)
3342
set(OLP_SDK_HTTP_IOS_SOURCES)
3443
endif()

olp-cpp-sdk-core/include/olp/core/context/Context.h

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019-2021 HERE Europe B.V.
2+
* Copyright (C) 2019-2023 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -170,7 +170,39 @@ class CORE_API Context {
170170
* @return The `android.content.Context` instance.
171171
*/
172172
static jobject getAndroidContext();
173-
#endif
173+
#elif defined(__APPLE__)
174+
/**
175+
* @brief Inform subscribers to enter background mode.
176+
*
177+
* @note Use it only after you initialize the `Context` class.
178+
*/
179+
static void EnterBackground();
180+
181+
/**
182+
* @brief Inform subscribers to exit background mode.
183+
*
184+
* @note Use it only after you initialize the `Context` class.
185+
*/
186+
static void ExitBackground();
187+
188+
/**
189+
* @brief Store background sessions completion handler to call it
190+
* when the session is done. Received from the OS by the application delegate.
191+
* See iOS downloading files in the background documentation for more details.
192+
*
193+
* @param session_name Name of the background session to store completion
194+
* handler for.
195+
* @param completion_handler A completion handler recieved from iOS
196+
* by the application delegate to be called when the background activity
197+
* related to a session is done.
198+
*
199+
* @note Use it only after you initialize the `Context` class.
200+
*/
201+
static void StoreBackgroundSessionCompletionHandler(
202+
const std::string& session_name,
203+
std::function<void(void)> completion_handler);
204+
205+
#endif // __APPLE__
174206
};
175207

176208
} // namespace context
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) 2023 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
#pragma once
21+
22+
namespace olp {
23+
namespace context {
24+
25+
/**
26+
* @brief Used only for the iOS environment to be informed that the application
27+
* is entering or exiting background.
28+
*/
29+
class EnterBackgroundSubscriber {
30+
public:
31+
/**
32+
* @brief Called when `Context` is entering background mode.
33+
*/
34+
virtual void OnEnterBackground() = 0;
35+
36+
/**
37+
* @brief Called when `Context` is exiting background mode.
38+
*/
39+
virtual void OnExitBackground() = 0;
40+
};
41+
42+
} // namespace context
43+
} // namespace olp

olp-cpp-sdk-core/src/context/Context.cpp

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019 HERE Europe B.V.
2+
* Copyright (C) 2019-2023 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,51 +33,30 @@
3333
#include <jni.h>
3434
#endif
3535

36+
#include "ContextInternal.h"
37+
3638
namespace {
3739
std::atomic_flag gDeInitializing = ATOMIC_FLAG_INIT;
3840
}
3941

4042
namespace olp {
4143
namespace context {
42-
// Because of static initializer orders, we need to have this on the heap.
43-
struct ContextData {
44-
std::vector<olp::context::Context::InitializedCallback> initVector;
45-
std::vector<olp::context::Context::DeinitializedCallback> deinitVector;
46-
47-
std::mutex contextMutex;
48-
size_t contextInstanceCounter = 0;
49-
#ifdef ANDROID
50-
JavaVM* javaVM = nullptr;
51-
jobject context = nullptr;
52-
#endif
53-
};
54-
55-
#define LOGTAG "Context"
56-
57-
std::shared_ptr<ContextData> instance() {
58-
// Static initialization is thread safe.
59-
// Shared pointer allows to extend the life of ContextData
60-
// until all Scope objects are destroyed.
61-
static std::shared_ptr<ContextData> gSetupVectors =
62-
std::make_shared<ContextData>();
63-
return gSetupVectors;
64-
}
6544

6645
void initialize() {
67-
const auto data = instance();
46+
const auto data = Instance();
6847

6948
// Fire all initializations.
70-
for (const olp::context::Context::InitializedCallback& cb : data->initVector)
49+
for (const olp::context::Context::InitializedCallback& cb : data->init_vector)
7150
cb();
7251
}
7352

7453
void deinitialize() {
7554
if (!atomic_flag_test_and_set(&gDeInitializing)) {
76-
const auto data = instance();
55+
const auto data = Instance();
7756

7857
// Fire all deinitializeds.
7958
for (const olp::context::Context::DeinitializedCallback& cb :
80-
data->deinitVector)
59+
data->deinit_vector)
8160
cb();
8261

8362
atomic_flag_clear(&gDeInitializing);
@@ -86,16 +65,16 @@ void deinitialize() {
8665

8766
void Context::addInitializeCallbacks(InitializedCallback initCallback,
8867
DeinitializedCallback deinitCalback) {
89-
auto cd = instance();
68+
auto cd = Instance();
9069

91-
cd->initVector.emplace_back(std::move(initCallback));
92-
cd->deinitVector.emplace_back(std::move(deinitCalback));
70+
cd->init_vector.emplace_back(std::move(initCallback));
71+
cd->deinit_vector.emplace_back(std::move(deinitCalback));
9372
}
9473

9574
void Context::init() {
96-
auto cd = instance();
75+
auto cd = Instance();
9776
#ifdef ANDROID
98-
cd->javaVM = nullptr;
77+
cd->java_vm = nullptr;
9978
#else
10079
(void)cd;
10180
#endif
@@ -104,16 +83,16 @@ void Context::init() {
10483

10584
void Context::deinit() {
10685
#ifdef ANDROID
107-
auto cd = instance();
86+
auto cd = Instance();
10887
// Release the global reference of Context.
10988
// Technically not needed if we took the application context,
11089
// but good to release just in case.
111-
if (cd->javaVM) {
90+
if (cd->java_vm) {
11291
JNIEnv* env = nullptr;
11392
bool attached = false;
114-
if (cd->javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) !=
93+
if (cd->java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) !=
11594
JNI_OK) {
116-
if (cd->javaVM->AttachCurrentThread(&env, nullptr) != JNI_OK) {
95+
if (cd->java_vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
11796
env = nullptr;
11897
} else {
11998
attached = true;
@@ -122,20 +101,20 @@ void Context::deinit() {
122101
if (env) {
123102
env->DeleteGlobalRef(cd->context);
124103
if (attached) {
125-
cd->javaVM->DetachCurrentThread();
104+
cd->java_vm->DetachCurrentThread();
126105
}
127106
}
128107
}
129108

130-
cd->javaVM = nullptr;
109+
cd->java_vm = nullptr;
131110
#endif
132111
deinitialize();
133112
}
134113

135114
#if defined(ANDROID)
136115
void Context::init(JavaVM* vm, jobject context) {
137-
auto cd = instance();
138-
cd->javaVM = vm;
116+
auto cd = Instance();
117+
cd->java_vm = vm;
139118

140119
JNIEnv* env = nullptr;
141120
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -148,46 +127,84 @@ void Context::init(JavaVM* vm, jobject context) {
148127
}
149128

150129
JavaVM* Context::getJavaVM() {
151-
auto cd = instance();
152-
if (!cd->javaVM) {
130+
auto cd = Instance();
131+
if (!cd->java_vm) {
153132
assert(false);
154133
}
155134

156-
return cd->javaVM;
135+
return cd->java_vm;
157136
}
158137

159138
jobject Context::getAndroidContext() {
160-
auto cd = instance();
139+
auto cd = Instance();
161140
if (!cd->context) {
162141
assert(false);
163142
}
164143
return cd->context;
165144
}
166-
#endif
145+
#elif defined(__APPLE__)
146+
void Context::EnterBackground() {
147+
auto cd = Instance();
148+
std::lock_guard<std::mutex> lock(cd->context_mutex);
149+
150+
auto& clients = cd->enter_background_subscibers;
151+
std::for_each(clients.begin(), clients.end(),
152+
[&](std::weak_ptr<EnterBackgroundSubscriber> weak) {
153+
auto strong = weak.lock();
154+
if (strong) {
155+
strong->OnEnterBackground();
156+
}
157+
});
158+
}
159+
160+
void Context::ExitBackground() {
161+
auto cd = Instance();
162+
std::lock_guard<std::mutex> lock(cd->context_mutex);
163+
164+
auto& clients = cd->enter_background_subscibers;
165+
std::for_each(clients.begin(), clients.end(),
166+
[&](std::weak_ptr<EnterBackgroundSubscriber> weak) {
167+
auto strong = weak.lock();
168+
if (strong) {
169+
strong->OnExitBackground();
170+
}
171+
});
172+
}
173+
174+
void Context::StoreBackgroundSessionCompletionHandler(
175+
const std::string& session_name,
176+
std::function<void(void)> completion_handler) {
177+
auto cd = Instance();
178+
std::lock_guard<std::mutex> lock(cd->context_mutex);
179+
180+
auto& completion_handlers = cd->completion_handlers;
181+
completion_handlers.emplace(session_name, std::move(completion_handler));
182+
}
183+
#endif // __APPLE__
167184

168-
Context::Scope::Scope() : m_cd(instance()) {
169-
std::lock_guard<std::mutex> lock(m_cd->contextMutex);
170-
assert(m_cd->contextInstanceCounter != std::numeric_limits<size_t>::max());
171-
if (m_cd->contextInstanceCounter++ == 0) {
185+
Context::Scope::Scope() : m_cd(Instance()) {
186+
std::lock_guard<std::mutex> lock(m_cd->context_mutex);
187+
assert(m_cd->context_instance_counter != std::numeric_limits<size_t>::max());
188+
if (m_cd->context_instance_counter++ == 0) {
172189
Context::init();
173190
}
174191
}
175192
#ifdef ANDROID
176193
/// see Context::init()
177-
Context::Scope::Scope(JavaVM* vm, jobject application) : m_cd(instance()) {
178-
std::lock_guard<std::mutex> lock(m_cd->contextMutex);
179-
assert(m_cd->contextInstanceCounter != std::numeric_limits<size_t>::max());
180-
if (m_cd->contextInstanceCounter++ == 0) {
194+
Context::Scope::Scope(JavaVM* vm, jobject application) : m_cd(Instance()) {
195+
std::lock_guard<std::mutex> lock(m_cd->context_mutex);
196+
assert(m_cd->context_instance_counter != std::numeric_limits<size_t>::max());
197+
if (m_cd->context_instance_counter++ == 0) {
181198
Context::init(vm, application);
182199
}
183200
}
184201
#endif
185202

186203
/// see Context::deinit()
187204
Context::Scope::~Scope() {
188-
std::lock_guard<std::mutex> lock(m_cd->contextMutex);
189-
assert(m_cd->contextInstanceCounter != std::numeric_limits<size_t>::min());
190-
if (--m_cd->contextInstanceCounter == 0) {
205+
std::lock_guard<std::mutex> lock(m_cd->context_mutex);
206+
assert(m_cd->context_instance_counter != std::numeric_limits<size_t>::min());
207+
if (--m_cd->context_instance_counter == 0) {
191208
Context::deinit();
192209
}
193210
}

0 commit comments

Comments
 (0)