19
19
#include " app/instance_id/iid_data_generated.h"
20
20
#include " app/src/app_identifier.h"
21
21
#include " app/src/cleanup_notifier.h"
22
+ #include " app/src/time.h"
23
+ #include " flatbuffers/flexbuffers.h"
22
24
23
25
namespace firebase {
24
26
namespace instance_id {
25
27
namespace internal {
26
28
27
29
using firebase::app::secure::UserSecureManager;
28
30
31
+ // Check-in backend.
32
+ static const char kCheckinUrl [] =
33
+ " https://device-provisioning.googleapis.com/checkin" ;
34
+ // Check-in refresh period (7 days) in milliseconds.
35
+ static const uint64_t kCheckinRefreshPeriodMs =
36
+ 7 * 24 * 60 * firebase::internal::kMillisecondsPerMinute ;
37
+ // Request type and protocol for check-in requests.
38
+ static const int kCheckinRequestType = 2 ;
39
+ static const int kCheckinProtocolVersion = 2 ;
40
+ // Instance ID backend.
41
+ static const char kInstanceIdUrl [] = " https://fcmtoken.googleapis.com/register" ;
42
+
29
43
std::map<App*, InstanceIdDesktopImpl*>
30
44
InstanceIdDesktopImpl::instance_id_by_app_; // NOLINT
31
45
Mutex InstanceIdDesktopImpl::instance_id_by_app_mutex_; // NOLINT
32
46
33
47
InstanceIdDesktopImpl::InstanceIdDesktopImpl (App* app)
34
- : storage_semaphore_(0 ), app_(app) {
48
+ : storage_semaphore_(0 ),
49
+ app_ (app),
50
+ locale_(" en_US" /* TODO(b/132732303) */ ),
51
+ timezone_(" America/Los_Angeles" /* TODO(b/132733022) */ ),
52
+ logging_id_(rand()), // NOLINT
53
+ ios_device_model_(" iPhone 8" /* TODO */ ),
54
+ ios_device_version_(" 8.0" /* TODO */ ),
55
+ network_operation_complete_(0 ),
56
+ terminating_(false ) {
57
+ rest::InitTransportCurl ();
58
+ transport_.reset (new rest::TransportCurl ());
59
+ (void )kInstanceIdUrl ; // TODO(smiles): Remove this when registration is in.
60
+
35
61
future_manager ().AllocFutureApi (this , kInstanceIdFnCount );
36
62
37
63
std::string package_name = app->options ().package_name ();
@@ -66,6 +92,19 @@ InstanceIdDesktopImpl::InstanceIdDesktopImpl(App* app)
66
92
}
67
93
68
94
InstanceIdDesktopImpl::~InstanceIdDesktopImpl () {
95
+ // Cancel any pending network operations.
96
+ {
97
+ MutexLock lock (network_operation_mutex_);
98
+ // All outstanding operations should complete with an error.
99
+ terminating_ = true ;
100
+ NetworkOperation* operation = network_operation_.get ();
101
+ if (operation) operation->Cancel ();
102
+ }
103
+ // Cancel scheduled tasks and shut down the scheduler to prevent any
104
+ // additional tasks being executed.
105
+ scheduler_.CancelAllAndShutdownWorkerThread ();
106
+
107
+ rest::CleanupTransportCurl ();
69
108
{
70
109
MutexLock lock (instance_id_by_app_mutex_);
71
110
auto it = instance_id_by_app_.find (app_);
@@ -77,9 +116,6 @@ InstanceIdDesktopImpl::~InstanceIdDesktopImpl() {
77
116
CleanupNotifier* notifier = CleanupNotifier::FindByOwner (app_);
78
117
assert (notifier);
79
118
notifier->UnregisterObject (this );
80
-
81
- // Make sure all the pending REST requests are either cancelled or resolved
82
- // here.
83
119
}
84
120
85
121
InstanceIdDesktopImpl* InstanceIdDesktopImpl::GetInstance (App* app) {
@@ -147,12 +183,15 @@ Future<void> InstanceIdDesktopImpl::DeleteTokenLastResult() {
147
183
148
184
// Save the instance ID to local secure storage.
149
185
bool InstanceIdDesktopImpl::SaveToStorage () {
186
+ if (terminating_) return false ;
187
+
150
188
// Build up a serialized buffer algorithmically:
151
189
flatbuffers::FlatBufferBuilder builder;
152
190
153
191
auto iid_data_table = CreateInstanceIdDesktopDataDirect (
154
192
builder, instance_id_.c_str (), checkin_data_.device_id .c_str (),
155
- checkin_data_.security_token .c_str (), expiration_time_);
193
+ checkin_data_.security_token .c_str (), checkin_data_.digest .c_str (),
194
+ checkin_data_.last_checkin_time_ms );
156
195
builder.Finish (iid_data_table);
157
196
158
197
std::string save_string;
@@ -195,6 +234,8 @@ bool InstanceIdDesktopImpl::LoadFromStorage() {
195
234
196
235
// Delete the instance ID from local secure storage.
197
236
bool InstanceIdDesktopImpl::DeleteFromStorage () {
237
+ if (terminating_) return false ;
238
+
198
239
Future<void > future = user_secure_manager_->DeleteUserData (app_->name ());
199
240
200
241
future.OnCompletion (
@@ -233,7 +274,151 @@ bool InstanceIdDesktopImpl::ReadStoredInstanceIdData(
233
274
instance_id_ = iid_data_fb->instance_id ()->c_str ();
234
275
checkin_data_.device_id = iid_data_fb->device_id ()->c_str ();
235
276
checkin_data_.security_token = iid_data_fb->security_token ()->c_str ();
236
- expiration_time_ = iid_data_fb->expiration_time ();
277
+ checkin_data_.digest = iid_data_fb->digest ()->c_str ();
278
+ checkin_data_.last_checkin_time_ms = iid_data_fb->last_checkin_time_ms ();
279
+ return true ;
280
+ }
281
+
282
+ bool InstanceIdDesktopImpl::InitialOrRefreshCheckin () {
283
+ if (terminating_) return false ;
284
+
285
+ // Load check-in data from storage if it hasn't already been loaded.
286
+ if (checkin_data_.last_checkin_time_ms == 0 ) {
287
+ // Try loading from storage. Since we can't tell whether this failed
288
+ // because the data doesn't exist or if there is an I/O error, just
289
+ // continue.
290
+ LoadFromStorage ();
291
+ }
292
+
293
+ // If we've already checked in.
294
+ if (checkin_data_.last_checkin_time_ms > 0 ) {
295
+ FIREBASE_ASSERT (!checkin_data_.device_id .empty () &&
296
+ !checkin_data_.security_token .empty () &&
297
+ !checkin_data_.digest .empty ());
298
+ // Make sure the device ID and token aren't expired.
299
+ uint64_t time_elapsed_ms =
300
+ firebase::internal::GetTimestamp () - checkin_data_.last_checkin_time_ms ;
301
+ if (time_elapsed_ms < kCheckinRefreshPeriodMs ) {
302
+ // Everything is up to date.
303
+ return true ;
304
+ }
305
+ checkin_data_.Clear ();
306
+ }
307
+
308
+ // Construct the JSON request.
309
+ flexbuffers::Builder fbb;
310
+ struct BuilderScope {
311
+ BuilderScope (flexbuffers::Builder* fbb_, const CheckinData* checkin_data_,
312
+ const char * locale_, const char * timezone_, int logging_id_,
313
+ const char * ios_device_model_, const char * ios_device_version_)
314
+ : fbb(fbb_),
315
+ checkin_data (checkin_data_),
316
+ locale(locale_),
317
+ timezone(timezone_),
318
+ logging_id(logging_id_),
319
+ ios_device_model(ios_device_model_),
320
+ ios_device_version(ios_device_version_) {}
321
+
322
+ flexbuffers::Builder* fbb;
323
+ const CheckinData* checkin_data;
324
+ const char * locale;
325
+ const char * timezone;
326
+ int logging_id;
327
+ const char * ios_device_model;
328
+ const char * ios_device_version;
329
+ } builder_scope(&fbb, &checkin_data_, locale_.c_str(), timezone_.c_str(),
330
+ logging_id_, ios_device_model_.c_str(),
331
+ ios_device_version_.c_str());
332
+ fbb.Map(
333
+ [](BuilderScope& builder_scope) {
334
+ flexbuffers::Builder* fbb = builder_scope.fbb ;
335
+ const CheckinData& checkin_data = *builder_scope.checkin_data ;
336
+ fbb->Map (
337
+ " checkin" ,
338
+ [](BuilderScope& builder_scope) {
339
+ flexbuffers::Builder* fbb = builder_scope.fbb ;
340
+ const CheckinData& checkin_data = *builder_scope.checkin_data ;
341
+ fbb->Map (
342
+ " iosbuild" ,
343
+ [](BuilderScope& builder_scope) {
344
+ flexbuffers::Builder* fbb = builder_scope.fbb ;
345
+ fbb->String (" model" , builder_scope.ios_device_model );
346
+ fbb->String (" os_version" , builder_scope.ios_device_version );
347
+ },
348
+ builder_scope);
349
+ fbb->Int (" type" , kCheckinRequestType );
350
+ fbb->Int (" user_number" , 0 /* unused at the moment */ );
351
+ fbb->Int (" last_checkin_msec" , checkin_data.last_checkin_time_ms );
352
+ },
353
+ builder_scope);
354
+ fbb->Int (" fragment" , 0 /* unused at the moment */ );
355
+ fbb->Int (" logging_id" , builder_scope.logging_id );
356
+ fbb->String (" locale" , builder_scope.locale );
357
+ fbb->Int (" version" , kCheckinProtocolVersion );
358
+ fbb->String (" digest" , checkin_data.digest .c_str ());
359
+ fbb->String (" timezone" , builder_scope.timezone );
360
+ fbb->Int (" user_serial_number" , 0 /* unused at the moment */ );
361
+ fbb->Int (" id" ,
362
+ flatbuffers::StringToInt (checkin_data.device_id .c_str ()));
363
+ fbb->Int (" security_token" ,
364
+ flatbuffers::StringToInt (checkin_data.security_token .c_str ()));
365
+ },
366
+ builder_scope);
367
+ fbb.Finish();
368
+ request_buffer_.clear();
369
+ flexbuffers::GetRoot (fbb.GetBuffer()).ToString(true , true , request_buffer_);
370
+ // Send request to the server then wait for the response or for the request
371
+ // to be canceled by another thread.
372
+ {
373
+ MutexLock lock (network_operation_mutex_);
374
+ network_operation_.reset (
375
+ new NetworkOperation (request_buffer_, &network_operation_complete_));
376
+ rest::Request* request = &network_operation_->request ;
377
+ request->set_url (kCheckinUrl );
378
+ request->set_method (rest::util::kPost );
379
+ request->add_header (rest::util::kContentType , rest::util::kApplicationJson );
380
+ network_operation_->Perform (transport_.get ());
381
+ }
382
+ network_operation_complete_.Wait();
383
+
384
+ logging_id_ = rand(); // NOLINT
385
+ {
386
+ MutexLock lock (network_operation_mutex_);
387
+ assert (network_operation_.get ());
388
+ const rest::Response& response = network_operation_->response ;
389
+ // Check for errors
390
+ if (response.status () != rest::util::HttpSuccess) {
391
+ LogError (" Check-in failed with response %d '%s'" , response.status (),
392
+ response.GetBody ());
393
+ network_operation_.reset (nullptr );
394
+ return false ;
395
+ }
396
+ // Parse the response.
397
+ flexbuffers::Builder fbb;
398
+ flatbuffers::Parser parser;
399
+ if (!parser.ParseFlexBuffer (response.GetBody (), nullptr , &fbb)) {
400
+ LogError (" Unable to parse response '%s'" , response.GetBody ());
401
+ network_operation_.reset (nullptr );
402
+ return false ;
403
+ }
404
+ auto root = flexbuffers::GetRoot (fbb.GetBuffer ()).AsMap ();
405
+ if (!root[" stats_ok" ].AsBool ()) {
406
+ LogError (" Unexpected stats_ok field '%s' vs 'true'" ,
407
+ root[" stats_ok" ].ToString ().c_str ());
408
+ network_operation_.reset (nullptr );
409
+ return false ;
410
+ }
411
+ checkin_data_.device_id = root[" android_id" ].AsString ().c_str ();
412
+ checkin_data_.security_token = root[" security_token" ].AsString ().c_str ();
413
+ checkin_data_.digest = root[" digest" ].AsString ().c_str ();
414
+ checkin_data_.last_checkin_time_ms = firebase::internal::GetTimestamp ();
415
+ network_operation_.reset (nullptr );
416
+ if (!SaveToStorage ()) {
417
+ checkin_data_.Clear ();
418
+ LogError (" Unable to save check-in information to storage." );
419
+ return false ;
420
+ }
421
+ }
237
422
return true ;
238
423
}
239
424
0 commit comments