1 //
2 // Copyright (C) 2014 The Android Open Source Project
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 
17 #include "shill/cellular/mobile_operator_info_impl.h"
18 
19 #include <regex.h>
20 
21 #include <algorithm>
22 #include <cctype>
23 #include <map>
24 
25 #include <base/bind.h>
26 #include <base/strings/string_util.h>
27 #include <google/protobuf/repeated_field.h>
28 
29 #include "shill/logging.h"
30 #include "shill/protobuf_lite_streams.h"
31 
32 using base::Bind;
33 using base::FilePath;
34 using google::protobuf::io::CopyingInputStreamAdaptor;
35 using google::protobuf::RepeatedField;
36 using google::protobuf::RepeatedPtrField;
37 using shill::mobile_operator_db::Data;
38 using shill::mobile_operator_db::Filter;
39 using shill::mobile_operator_db::LocalizedName;
40 using shill::mobile_operator_db::MobileAPN;
41 using shill::mobile_operator_db::MobileNetworkOperator;
42 using shill::mobile_operator_db::MobileOperatorDB;
43 using shill::mobile_operator_db::MobileVirtualNetworkOperator;
44 using shill::mobile_operator_db::OnlinePortal;
45 using std::map;
46 using std::string;
47 using std::vector;
48 
49 namespace shill {
50 
51 namespace Logging {
52 static auto kModuleLogScope = ScopeLogger::kCellular;
ObjectID(const MobileOperatorInfoImpl * m)53 static string ObjectID(const MobileOperatorInfoImpl* m) {
54   return "(mobile_operator_info_impl)";
55 }
56 }
57 
58 // static
59 const char* MobileOperatorInfoImpl::kDefaultDatabasePath =
60     "/usr/share/shill/serviceproviders.pbf";
61 const int MobileOperatorInfoImpl::kMCCMNCMinLen = 5;
62 
63 namespace {
64 
65 // Wrap some low level functions from the GNU regex librarly.
GetRegError(int code,const regex_t * compiled)66 string GetRegError(int code, const regex_t* compiled) {
67   size_t length = regerror(code, compiled, nullptr, 0);
68   vector<char> buffer(length);
69   DCHECK_EQ(length, regerror(code, compiled, buffer.data(), length));
70   return buffer.data();
71 }
72 
73 }  // namespace
74 
MobileOperatorInfoImpl(EventDispatcher * dispatcher,const string & info_owner)75 MobileOperatorInfoImpl::MobileOperatorInfoImpl(EventDispatcher* dispatcher,
76                                                const string& info_owner)
77     : dispatcher_(dispatcher),
78       info_owner_(info_owner),
79       observers_(
80           base::ObserverList<MobileOperatorInfo::Observer, true>::NOTIFY_ALL),
81       operator_code_type_(kOperatorCodeTypeUnknown),
82       current_mno_(nullptr),
83       current_mvno_(nullptr),
84       requires_roaming_(false),
85       user_olp_empty_(true),
86       weak_ptr_factory_(this) {
87   AddDatabasePath(FilePath(kDefaultDatabasePath));
88 }
89 
~MobileOperatorInfoImpl()90 MobileOperatorInfoImpl::~MobileOperatorInfoImpl() {}
91 
ClearDatabasePaths()92 void MobileOperatorInfoImpl::ClearDatabasePaths() {
93   database_paths_.clear();
94 }
95 
AddDatabasePath(const FilePath & absolute_path)96 void MobileOperatorInfoImpl::AddDatabasePath(const FilePath& absolute_path) {
97   database_paths_.push_back(absolute_path);
98 }
99 
Init()100 bool MobileOperatorInfoImpl::Init() {
101   // |database_| is guaranteed to be set once |Init| is called.
102   database_.reset(new MobileOperatorDB());
103 
104   bool found_databases = false;
105   for (const auto& database_path : database_paths_) {
106     const char* database_path_cstr = database_path.value().c_str();
107     std::unique_ptr<CopyingInputStreamAdaptor> database_stream;
108     database_stream.reset(protobuf_lite_file_input_stream(database_path_cstr));
109     if (!database_stream.get()) {
110       LOG(ERROR) << "Failed to read mobile operator database: "
111                  << database_path_cstr;
112       continue;
113     }
114 
115     MobileOperatorDB database;
116     if (!database.ParseFromZeroCopyStream(database_stream.get())) {
117       LOG(ERROR) << "Could not parse mobile operator database: "
118                  << database_path_cstr;
119       continue;
120     }
121     LOG(INFO) << "Successfully loaded database: " << database_path_cstr;
122     // Collate loaded databases into one as they're found.
123     // TODO(pprabhu) This merge might be very costly. Determine if we need to
124     // implement move semantics / bias the merge to use the largest database
125     // as the base database and merge other databases into it.
126     database_->MergeFrom(database);
127     found_databases = true;
128   }
129 
130   if (!found_databases) {
131     LOG(ERROR) << "Could not read any mobile operator database. "
132                << "Will not be able to determine MVNO.";
133     return false;
134   }
135 
136   PreprocessDatabase();
137   return true;
138 }
139 
AddObserver(MobileOperatorInfo::Observer * observer)140 void MobileOperatorInfoImpl::AddObserver(
141     MobileOperatorInfo::Observer* observer) {
142   observers_.AddObserver(observer);
143 }
144 
RemoveObserver(MobileOperatorInfo::Observer * observer)145 void MobileOperatorInfoImpl::RemoveObserver(
146     MobileOperatorInfo::Observer* observer) {
147   observers_.RemoveObserver(observer);
148 }
149 
IsMobileNetworkOperatorKnown() const150 bool MobileOperatorInfoImpl::IsMobileNetworkOperatorKnown() const {
151   return (current_mno_ != nullptr);
152 }
153 
IsMobileVirtualNetworkOperatorKnown() const154 bool MobileOperatorInfoImpl::IsMobileVirtualNetworkOperatorKnown() const {
155   return (current_mvno_ != nullptr);
156 }
157 
158 // ///////////////////////////////////////////////////////////////////////////
159 // Getters.
info_owner() const160 const string& MobileOperatorInfoImpl::info_owner() const {
161   return info_owner_;
162 }
163 
uuid() const164 const string& MobileOperatorInfoImpl::uuid() const {
165   return uuid_;
166 }
167 
operator_name() const168 const string& MobileOperatorInfoImpl::operator_name() const {
169   // TODO(pprabhu) I'm not very sure yet what is the right thing to do here.
170   // It is possible that we obtain a name OTA, and then using some other
171   // information (say the iccid range), determine that this is an MVNO. In
172   // that case, we may want to *override* |user_operator_name_| by the name
173   // obtained from the DB for the MVNO.
174   return operator_name_;
175 }
176 
country() const177 const string& MobileOperatorInfoImpl::country() const {
178   return country_;
179 }
180 
mccmnc() const181 const string& MobileOperatorInfoImpl::mccmnc() const {
182   return mccmnc_;
183 }
184 
sid() const185 const string& MobileOperatorInfoImpl::MobileOperatorInfoImpl::sid() const {
186   return sid_;
187 }
188 
nid() const189 const string& MobileOperatorInfoImpl::nid() const {
190   return (user_nid_ == "") ? nid_ : user_nid_;
191 }
192 
mccmnc_list() const193 const vector<string>& MobileOperatorInfoImpl::mccmnc_list() const {
194   return mccmnc_list_;
195 }
196 
sid_list() const197 const vector<string>& MobileOperatorInfoImpl::sid_list() const {
198   return sid_list_;
199 }
200 
201 const vector<MobileOperatorInfo::LocalizedName> &
operator_name_list() const202 MobileOperatorInfoImpl::operator_name_list() const {
203   return operator_name_list_;
204 }
205 
206 const ScopedVector<MobileOperatorInfo::MobileAPN> &
apn_list() const207 MobileOperatorInfoImpl::apn_list() const {
208   return apn_list_;
209 }
210 
211 const vector<MobileOperatorInfo::OnlinePortal> &
olp_list() const212 MobileOperatorInfoImpl::olp_list() const {
213   return olp_list_;
214 }
215 
activation_code() const216 const string& MobileOperatorInfoImpl::activation_code() const {
217   return activation_code_;
218 }
219 
requires_roaming() const220 bool MobileOperatorInfoImpl::requires_roaming() const {
221   return requires_roaming_;
222 }
223 
224 // ///////////////////////////////////////////////////////////////////////////
225 // Functions used to notify this object of operator data changes.
UpdateIMSI(const string & imsi)226 void MobileOperatorInfoImpl::UpdateIMSI(const string& imsi) {
227   bool operator_changed = false;
228   if (user_imsi_ == imsi) {
229     return;
230   }
231 
232   user_imsi_ = imsi;
233 
234   if (!user_mccmnc_.empty()) {
235     if (!base::StartsWith(imsi, user_mccmnc_,
236                           base::CompareCase::INSENSITIVE_ASCII)) {
237       LOG(WARNING) << "MCCMNC [" << user_mccmnc_ << "] is not a substring of "
238                    << "the IMSI [" << imsi << "].";
239     }
240   } else {
241     // Attempt to determine the MNO from IMSI since MCCMNC is absent.
242     AppendToCandidatesByMCCMNC(imsi.substr(0, kMCCMNCMinLen));
243     AppendToCandidatesByMCCMNC(imsi.substr(0, kMCCMNCMinLen + 1));
244     if (!candidates_by_operator_code_.empty()) {
245       // We found some candidates using IMSI.
246       operator_changed |= UpdateMNO();
247     }
248   }
249   operator_changed |= UpdateMVNO();
250 
251   // No special notification should be sent for this property, since the object
252   // does not expose |imsi| as a property at all.
253   if (operator_changed) {
254     PostNotifyOperatorChanged();
255   }
256 }
257 
UpdateICCID(const string & iccid)258 void MobileOperatorInfoImpl::UpdateICCID(const string& iccid) {
259   if (user_iccid_ == iccid) {
260     return;
261   }
262 
263   user_iccid_ = iccid;
264   // |iccid| is not an exposed property, so don't raise event for just this
265   // property update.
266   if (UpdateMVNO()) {
267     PostNotifyOperatorChanged();
268   }
269 }
270 
UpdateMCCMNC(const string & mccmnc)271 void MobileOperatorInfoImpl::UpdateMCCMNC(const string& mccmnc) {
272   if (user_mccmnc_ == mccmnc) {
273     return;
274   }
275 
276   user_mccmnc_ = mccmnc;
277   HandleMCCMNCUpdate();
278   candidates_by_operator_code_.clear();
279   AppendToCandidatesByMCCMNC(mccmnc);
280 
281   // Always update M[V]NO, even if we found no candidates, since we might have
282   // lost some candidates due to an incorrect MCCMNC.
283   bool operator_changed = false;
284   operator_changed |= UpdateMNO();
285   operator_changed |= UpdateMVNO();
286   if (operator_changed || ShouldNotifyPropertyUpdate()) {
287     PostNotifyOperatorChanged();
288   }
289 }
290 
UpdateSID(const string & sid)291 void MobileOperatorInfoImpl::UpdateSID(const string& sid) {
292   if (user_sid_ == sid) {
293     return;
294   }
295 
296   user_sid_ = sid;
297   HandleSIDUpdate();
298   candidates_by_operator_code_.clear();
299   AppendToCandidatesBySID(sid);
300 
301   // Always update M[V]NO, even if we found no candidates, since we might have
302   // lost some candidates due to an incorrect SID.
303   bool operator_changed = false;
304   operator_changed |= UpdateMNO();
305   operator_changed |= UpdateMVNO();
306   if (operator_changed || ShouldNotifyPropertyUpdate()) {
307     PostNotifyOperatorChanged();
308   }
309 }
310 
UpdateNID(const string & nid)311 void MobileOperatorInfoImpl::UpdateNID(const string& nid) {
312   if (user_nid_ == nid) {
313     return;
314   }
315 
316   user_nid_ = nid;
317   if (UpdateMVNO() || ShouldNotifyPropertyUpdate()) {
318     PostNotifyOperatorChanged();
319   }
320 }
321 
UpdateOperatorName(const string & operator_name)322 void MobileOperatorInfoImpl::UpdateOperatorName(const string& operator_name) {
323   bool operator_changed = false;
324   if (user_operator_name_ == operator_name) {
325     return;
326   }
327 
328   user_operator_name_ = operator_name;
329   HandleOperatorNameUpdate();
330 
331   // We must update the candidates by name anyway.
332   StringToMNOListMap::const_iterator cit = name_to_mnos_.find(
333       NormalizeOperatorName(operator_name));
334   candidates_by_name_.clear();
335   if (cit != name_to_mnos_.end()) {
336     candidates_by_name_ = cit->second;
337     // We should never have inserted an empty vector into the map.
338     DCHECK(!candidates_by_name_.empty());
339   } else {
340     LOG(INFO) << "Operator name [" << operator_name << "] "
341               << "(Normalized: [" << NormalizeOperatorName(operator_name)
342               << "]) does not match any MNO.";
343   }
344 
345   operator_changed |= UpdateMNO();
346   operator_changed |= UpdateMVNO();
347   if (operator_changed || ShouldNotifyPropertyUpdate()) {
348     PostNotifyOperatorChanged();
349   }
350 }
351 
UpdateOnlinePortal(const string & url,const string & method,const string & post_data)352 void MobileOperatorInfoImpl::UpdateOnlinePortal(const string& url,
353                                                 const string& method,
354                                                 const string& post_data) {
355   if (!user_olp_empty_ &&
356       user_olp_.url == url &&
357       user_olp_.method == method &&
358       user_olp_.post_data == post_data) {
359     return;
360   }
361 
362   user_olp_empty_ = false;
363   user_olp_.url = url;
364   user_olp_.method = method;
365   user_olp_.post_data = post_data;
366   HandleOnlinePortalUpdate();
367 
368   // OnlinePortal is never used in deciding M[V]NO.
369   if (ShouldNotifyPropertyUpdate()) {
370     PostNotifyOperatorChanged();
371   }
372 }
373 
Reset()374 void MobileOperatorInfoImpl::Reset() {
375   bool should_notify = current_mno_ != nullptr || current_mvno_ != nullptr;
376 
377   current_mno_ = nullptr;
378   current_mvno_ = nullptr;
379   operator_code_type_ = kOperatorCodeTypeUnknown;
380   candidates_by_operator_code_.clear();
381   candidates_by_name_.clear();
382 
383   ClearDBInformation();
384 
385   user_imsi_.clear();
386   user_iccid_.clear();
387   user_mccmnc_.clear();
388   user_sid_.clear();
389   user_nid_.clear();
390   user_operator_name_.clear();
391   user_olp_empty_ = true;
392   user_olp_.url.clear();
393   user_olp_.method.clear();
394   user_olp_.post_data.clear();
395 
396   if (should_notify) {
397     PostNotifyOperatorChanged();
398   }
399 }
400 
PreprocessDatabase()401 void MobileOperatorInfoImpl::PreprocessDatabase() {
402   SLOG(this, 3) << __func__;
403 
404   mccmnc_to_mnos_.clear();
405   sid_to_mnos_.clear();
406   name_to_mnos_.clear();
407 
408   const RepeatedPtrField<MobileNetworkOperator>& mnos = database_->mno();
409   for (const auto& mno : mnos) {
410     // MobileNetworkOperator::data is a required field.
411     DCHECK(mno.has_data());
412     const Data& data = mno.data();
413 
414     const RepeatedPtrField<string>& mccmncs = data.mccmnc();
415     for (const auto& mccmnc : mccmncs) {
416       InsertIntoStringToMNOListMap(&mccmnc_to_mnos_, mccmnc, &mno);
417     }
418 
419     const RepeatedPtrField<string>& sids = data.sid();
420     for (const auto& sid : sids) {
421       InsertIntoStringToMNOListMap(&sid_to_mnos_, sid, &mno);
422     }
423 
424     const RepeatedPtrField<LocalizedName>& localized_names =
425         data.localized_name();
426     for (const auto& localized_name : localized_names) {
427       // LocalizedName::name is a required field.
428       DCHECK(localized_name.has_name());
429       InsertIntoStringToMNOListMap(&name_to_mnos_,
430                                    NormalizeOperatorName(localized_name.name()),
431                                    &mno);
432     }
433   }
434 
435   if (database_->imvno_size() > 0) {
436     // TODO(pprabhu) Support IMVNOs.
437     LOG(ERROR) << "InternationalMobileVirtualNetworkOperators are not "
438                << "supported yet. Ignoring all IMVNOs.";
439   }
440 }
441 
442 // This function assumes that duplicate |values| are never inserted for the
443 // same |key|. If you do that, the function is too dumb to deduplicate the
444 // |value|s, and two copies will get stored.
InsertIntoStringToMNOListMap(StringToMNOListMap * table,const string & key,const MobileNetworkOperator * value)445 void MobileOperatorInfoImpl::InsertIntoStringToMNOListMap(
446     StringToMNOListMap* table,
447     const string& key,
448     const MobileNetworkOperator* value) {
449   (*table)[key].push_back(value);
450 }
451 
AppendToCandidatesByMCCMNC(const string & mccmnc)452 bool MobileOperatorInfoImpl::AppendToCandidatesByMCCMNC(const string& mccmnc) {
453   // First check that we haven't determined candidates using SID.
454   if (operator_code_type_ == kOperatorCodeTypeSID) {
455     LOG(WARNING) << "SID update will be overridden by the MCCMNC update for "
456                     "determining MNO.";
457     candidates_by_operator_code_.clear();
458   }
459 
460   operator_code_type_ = kOperatorCodeTypeMCCMNC;
461   StringToMNOListMap::const_iterator cit = mccmnc_to_mnos_.find(mccmnc);
462   if (cit == mccmnc_to_mnos_.end()) {
463     LOG(WARNING) << "Unknown MCCMNC value [" << mccmnc << "].";
464     return false;
465   }
466 
467   // We should never have inserted an empty vector into the map.
468   DCHECK(!cit->second.empty());
469   for (const auto& mno : cit->second) {
470     candidates_by_operator_code_.push_back(mno);
471   }
472   return true;
473 }
474 
AppendToCandidatesBySID(const string & sid)475 bool MobileOperatorInfoImpl::AppendToCandidatesBySID(const string& sid) {
476   // First check that we haven't determined candidates using MCCMNC.
477   if (operator_code_type_ == kOperatorCodeTypeMCCMNC) {
478     LOG(WARNING) << "MCCMNC update will be overriden by the SID update for "
479                     "determining MNO.";
480     candidates_by_operator_code_.clear();
481   }
482 
483   operator_code_type_ = kOperatorCodeTypeSID;
484   StringToMNOListMap::const_iterator cit = sid_to_mnos_.find(sid);
485   if (cit == sid_to_mnos_.end()) {
486     LOG(WARNING) << "Unknown SID value [" << sid << "].";
487     return false;
488   }
489 
490   // We should never have inserted an empty vector into the map.
491   DCHECK(!cit->second.empty());
492   for (const auto& mno : cit->second) {
493     candidates_by_operator_code_.push_back(mno);
494   }
495   return true;
496 }
497 
OperatorCodeString() const498 string MobileOperatorInfoImpl::OperatorCodeString() const {
499   switch (operator_code_type_) {
500     case kOperatorCodeTypeMCCMNC:
501       return "MCCMNC";
502     case kOperatorCodeTypeSID:
503       return "SID";
504     case kOperatorCodeTypeUnknown:  // FALLTHROUGH
505     default:
506       return "UnknownOperatorCodeType";
507   }
508 }
509 
UpdateMNO()510 bool MobileOperatorInfoImpl::UpdateMNO() {
511   SLOG(this, 3) << __func__;
512   const MobileNetworkOperator* candidate = nullptr;
513 
514   // The only way |operator_code_type_| can be |kOperatorCodeTypeUnknown| is
515   // that we haven't received any operator_code updates yet.
516   DCHECK(operator_code_type_ == kOperatorCodeTypeMCCMNC ||
517          operator_code_type_ == kOperatorCodeTypeSID ||
518          (user_mccmnc_.empty() && user_sid_.empty()));
519 
520   // TODO(pprabhu) Remove this despicable hack. (crosbug.com/p/30200)
521   // We currently have no principled way to handle an MVNO for which the
522   // database does not have MCCMNC data. It is possible that some other MNO
523   // matches the MCCMNC, while the MVNO matches the operator name. We special
524   // case one such operator here and override all the logic below.
525   const char kCubicUUID[] = "2de39b14-c3ba-4143-abb5-c67a390034ee";
526   for (auto candidate_by_name : candidates_by_name_) {
527     CHECK(candidate_by_name->has_data());
528     CHECK(candidate_by_name->data().has_uuid());
529     if (candidate_by_name->data().uuid() == kCubicUUID) {
530       current_mno_ = candidate_by_name;
531       RefreshDBInformation();
532       return true;
533     }
534   }
535 
536   if (candidates_by_operator_code_.size() == 1) {
537     candidate = candidates_by_operator_code_[0];
538     if (candidates_by_name_.size() > 0) {
539       bool found_match = false;
540       for (auto candidate_by_name : candidates_by_name_) {
541         if (candidate_by_name == candidate) {
542           found_match = true;
543           break;
544         }
545       }
546       if (!found_match) {
547         const string& operator_code =
548             (operator_code_type_ == kOperatorCodeTypeMCCMNC) ? user_mccmnc_ :
549                                                                user_sid_;
550         SLOG(this, 1) << "MNO determined by "
551                       << OperatorCodeString() << " [" << operator_code
552                       << "] does not match any suggested by name["
553                       << user_operator_name_
554                       << "]. "
555                       << OperatorCodeString() << " overrides name!";
556       }
557     }
558   } else if (candidates_by_operator_code_.size() > 1) {
559     // Try to find an intersection of the two candidate lists. These lists
560     // should be almost always of length 1. Simply iterate.
561     for (auto candidate_by_mccmnc : candidates_by_operator_code_) {
562       for (auto candidate_by_name : candidates_by_name_) {
563         if (candidate_by_mccmnc == candidate_by_name) {
564           candidate = candidate_by_mccmnc;
565           break;
566         }
567       }
568       if (candidate != nullptr) {
569         break;
570       }
571     }
572     if (candidate == nullptr) {
573       const string& operator_code =
574           (operator_code_type_ == kOperatorCodeTypeMCCMNC) ? user_mccmnc_ :
575                                                              user_sid_;
576       SLOG(this, 1) << "MNOs suggested by "
577                     << OperatorCodeString() << " [" << operator_code
578                     << "] are multiple and disjoint from those suggested "
579                     << "by name["
580                     << user_operator_name_
581                     << "].";
582       candidate = PickOneFromDuplicates(candidates_by_operator_code_);
583     }
584   } else {  // candidates_by_operator_code_.size() == 0
585     // Special case: In case we had a *wrong* operator_code update, we want
586     // to override the suggestions from |user_operator_name_|. We should not
587     // determine an MNO in this case.
588     if ((operator_code_type_ == kOperatorCodeTypeMCCMNC &&
589          !user_mccmnc_.empty()) ||
590         (operator_code_type_ == kOperatorCodeTypeSID && !user_sid_.empty())) {
591       SLOG(this, 1) << "A non-matching "
592                     << OperatorCodeString() << " "
593                     << "was reported by the user."
594                     << "We fail the MNO match in this case.";
595     } else if (candidates_by_name_.size() == 1) {
596       candidate = candidates_by_name_[0];
597     } else if (candidates_by_name_.size() > 1) {
598       SLOG(this, 1) << "Multiple MNOs suggested by name["
599                     << user_operator_name_
600                     << "], and none by MCCMNC.";
601       candidate = PickOneFromDuplicates(candidates_by_name_);
602     } else {  // candidates_by_name_.size() == 0
603       SLOG(this, 1) << "No candidates suggested.";
604     }
605   }
606 
607   if (candidate != current_mno_) {
608     current_mno_ = candidate;
609     RefreshDBInformation();
610     return true;
611   }
612   return false;
613 }
614 
UpdateMVNO()615 bool MobileOperatorInfoImpl::UpdateMVNO() {
616   SLOG(this, 3) << __func__;
617   if (current_mno_ == nullptr) {
618     return false;
619   }
620 
621   for (const auto& candidate_mvno : current_mno_->mvno()) {
622     bool passed_all_filters = true;
623     for (const auto& filter : candidate_mvno.mvno_filter()) {
624       if (!FilterMatches(filter)) {
625         passed_all_filters = false;
626         break;
627       }
628     }
629     if (passed_all_filters) {
630       if (current_mvno_ == &candidate_mvno) {
631         return false;
632       }
633       current_mvno_ = &candidate_mvno;
634       RefreshDBInformation();
635       return true;
636     }
637   }
638 
639   // We did not find any valid MVNO.
640   if (current_mvno_ != nullptr) {
641     current_mvno_ = nullptr;
642     RefreshDBInformation();
643     return true;
644   }
645   return false;
646 }
647 
PickOneFromDuplicates(const vector<const MobileNetworkOperator * > & duplicates) const648 const MobileNetworkOperator* MobileOperatorInfoImpl::PickOneFromDuplicates(
649     const vector<const MobileNetworkOperator*>& duplicates) const {
650   if (duplicates.empty())
651     return nullptr;
652 
653   for (auto candidate : duplicates) {
654     if (candidate->earmarked()) {
655       SLOG(this, 2) << "Picking earmarked candidate: "
656                     << candidate->data().uuid();
657       return candidate;
658     }
659   }
660   SLOG(this, 2) << "No earmarked candidate found. Choosing the first.";
661   return duplicates[0];
662 }
663 
FilterMatches(const Filter & filter)664 bool MobileOperatorInfoImpl::FilterMatches(const Filter& filter) {
665   DCHECK(filter.has_regex());
666   string to_match;
667   switch (filter.type()) {
668     case mobile_operator_db::Filter_Type_IMSI:
669       to_match = user_imsi_;
670       break;
671     case mobile_operator_db::Filter_Type_ICCID:
672       to_match = user_iccid_;
673       break;
674     case mobile_operator_db::Filter_Type_SID:
675       to_match = user_sid_;
676       break;
677     case mobile_operator_db::Filter_Type_OPERATOR_NAME:
678       to_match = user_operator_name_;
679       break;
680     case mobile_operator_db::Filter_Type_MCCMNC:
681       to_match = user_mccmnc_;
682       break;
683     default:
684       SLOG(this, 1) << "Unknown filter type [" << filter.type() << "]";
685       return false;
686   }
687   // |to_match| can be empty if we have no *user provided* information of the
688   // correct type.
689   if (to_match.empty()) {
690     SLOG(this, 2) << "Nothing to match against (filter: "
691                   << filter.regex() << ").";
692     return false;
693   }
694 
695   // Must use GNU regex implementation, since C++11 implementation is
696   // incomplete.
697   regex_t filter_regex;
698   string filter_regex_str = filter.regex();
699 
700   // |regexec| matches the given regular expression to a substring of the
701   // given query string. Ensure that |filter_regex_str| uses anchors to
702   // accept only a full match.
703   if (filter_regex_str.front() != '^') {
704     filter_regex_str = "^" + filter_regex_str;
705   }
706   if (filter_regex_str.back() != '$') {
707     filter_regex_str = filter_regex_str + "$";
708   }
709 
710   int regcomp_error = regcomp(&filter_regex,
711                               filter_regex_str.c_str(),
712                               REG_EXTENDED | REG_NOSUB);
713   if (regcomp_error) {
714     LOG(WARNING) << "Could not compile regex '" << filter.regex() << "'. "
715                  << "Error returned: "
716                  << GetRegError(regcomp_error, &filter_regex) << ". ";
717     regfree(&filter_regex);
718     return false;
719   }
720 
721   int regexec_error = regexec(&filter_regex,
722                               to_match.c_str(),
723                               0,
724                               nullptr,
725                               0);
726   if (regexec_error) {
727     string error_string;
728     error_string = GetRegError(regcomp_error, &filter_regex);
729     SLOG(this, 2) << "Could not match string " << to_match << " "
730                   << "against regexp " << filter.regex() << ". "
731                   << "Error returned: " << error_string << ". ";
732     regfree(&filter_regex);
733     return false;
734   }
735   regfree(&filter_regex);
736   return true;
737 }
738 
RefreshDBInformation()739 void MobileOperatorInfoImpl::RefreshDBInformation() {
740   ClearDBInformation();
741 
742   if (current_mno_ == nullptr) {
743     return;
744   }
745 
746   // |data| is a required field.
747   DCHECK(current_mno_->has_data());
748   SLOG(this, 2) << "Reloading MNO data.";
749   ReloadData(current_mno_->data());
750 
751   if (current_mvno_ != nullptr) {
752     // |data| is a required field.
753     DCHECK(current_mvno_->has_data());
754     SLOG(this, 2) << "Reloading MVNO data.";
755     ReloadData(current_mvno_->data());
756   }
757 }
758 
ClearDBInformation()759 void MobileOperatorInfoImpl::ClearDBInformation() {
760   uuid_.clear();
761   country_.clear();
762   nid_.clear();
763   mccmnc_list_.clear();
764   HandleMCCMNCUpdate();
765   sid_list_.clear();
766   HandleSIDUpdate();
767   operator_name_list_.clear();
768   HandleOperatorNameUpdate();
769   apn_list_.clear();
770   olp_list_.clear();
771   raw_olp_list_.clear();
772   HandleOnlinePortalUpdate();
773   activation_code_.clear();
774   requires_roaming_ = false;
775 }
776 
ReloadData(const Data & data)777 void MobileOperatorInfoImpl::ReloadData(const Data& data) {
778   SLOG(this, 3) << __func__;
779   // |uuid_| is *always* overwritten. An MNO and MVNO should not share the
780   // |uuid_|.
781   CHECK(data.has_uuid());
782   uuid_ = data.uuid();
783 
784   if (data.has_country()) {
785     country_ = data.country();
786   }
787 
788   if (data.localized_name_size() > 0) {
789     operator_name_list_.clear();
790     for (const auto& localized_name : data.localized_name()) {
791       operator_name_list_.push_back({localized_name.name(),
792                                      localized_name.language()});
793     }
794     HandleOperatorNameUpdate();
795   }
796 
797   if (data.has_requires_roaming()) {
798     requires_roaming_ = data.requires_roaming();
799   }
800 
801   if (data.olp_size() > 0) {
802     raw_olp_list_.clear();
803     // Copy the olp list so we can mutate it.
804     for (const auto& olp : data.olp()) {
805       raw_olp_list_.push_back(olp);
806     }
807     HandleOnlinePortalUpdate();
808   }
809 
810   if (data.mccmnc_size() > 0) {
811     mccmnc_list_.clear();
812     for (const auto& mccmnc : data.mccmnc()) {
813       mccmnc_list_.push_back(mccmnc);
814     }
815     HandleMCCMNCUpdate();
816   }
817 
818   if (data.mobile_apn_size() > 0) {
819     apn_list_.clear();
820     for (const auto& apn_data : data.mobile_apn()) {
821       auto* apn = new MobileOperatorInfo::MobileAPN();
822       apn->apn = apn_data.apn();
823       apn->username = apn_data.username();
824       apn->password = apn_data.password();
825       for (const auto& localized_name : apn_data.localized_name()) {
826         apn->operator_name_list.push_back({localized_name.name(),
827                                            localized_name.language()});
828       }
829 
830       // Takes ownership.
831       apn_list_.push_back(apn);
832     }
833   }
834 
835   if (data.sid_size() > 0) {
836     sid_list_.clear();
837     for (const auto& sid : data.sid()) {
838       sid_list_.push_back(sid);
839     }
840     HandleSIDUpdate();
841   }
842 
843   if (data.has_activation_code()) {
844     activation_code_ = data.activation_code();
845   }
846 }
847 
HandleMCCMNCUpdate()848 void MobileOperatorInfoImpl::HandleMCCMNCUpdate() {
849   if (!user_mccmnc_.empty()) {
850     bool append_to_list = true;
851     for (const auto& mccmnc : mccmnc_list_) {
852       append_to_list &= (user_mccmnc_ != mccmnc);
853     }
854     if (append_to_list) {
855       mccmnc_list_.push_back(user_mccmnc_);
856     }
857   }
858 
859   if (!user_mccmnc_.empty()) {
860     mccmnc_ = user_mccmnc_;
861   } else if (mccmnc_list_.size() > 0) {
862     mccmnc_ = mccmnc_list_[0];
863   } else {
864     mccmnc_.clear();
865   }
866 }
867 
HandleOperatorNameUpdate()868 void MobileOperatorInfoImpl::HandleOperatorNameUpdate() {
869   if (!user_operator_name_.empty()) {
870     bool append_user_operator_name = true;
871     for (const auto& localized_name : operator_name_list_) {
872       append_user_operator_name &= (user_operator_name_ != localized_name.name);
873     }
874     if (append_user_operator_name) {
875       MobileOperatorInfo::LocalizedName localized_name {
876           user_operator_name_,
877           ""};
878       operator_name_list_.push_back(localized_name);
879     }
880   }
881 
882   if (!operator_name_list_.empty()) {
883     operator_name_ = operator_name_list_[0].name;
884   } else if (!user_operator_name_.empty()) {
885     operator_name_ = user_operator_name_;
886   } else {
887     operator_name_.clear();
888   }
889 }
890 
HandleSIDUpdate()891 void MobileOperatorInfoImpl::HandleSIDUpdate() {
892   if (!user_sid_.empty()) {
893     bool append_user_sid = true;
894     for (const auto& sid : sid_list_) {
895       append_user_sid &= (user_sid_ != sid);
896     }
897     if (append_user_sid) {
898       sid_list_.push_back(user_sid_);
899     }
900   }
901 
902   if (!user_sid_.empty()) {
903     sid_ = user_sid_;
904   } else if (sid_list_.size() > 0) {
905     sid_ = sid_list_[0];
906   } else {
907     sid_.clear();
908   }
909 }
910 
911 // Warning: Currently, an MCCMNC/SID update by itself does not result into
912 // recomputation of the |olp_list_|. This means that if the new MCCMNC/SID
913 // causes an online portal filter to match, we'll miss that.
914 // This won't be a problem if either the MNO or the MVNO changes, since data is
915 // reloaded then.
916 // This is a corner case that we don't expect to hit, since MCCMNC doesn't
917 // really change in a running system.
HandleOnlinePortalUpdate()918 void MobileOperatorInfoImpl::HandleOnlinePortalUpdate() {
919   // Always recompute |olp_list_|. We don't expect this list to be big.
920   olp_list_.clear();
921   for (const auto& raw_olp : raw_olp_list_) {
922     if (!raw_olp.has_olp_filter() || FilterMatches(raw_olp.olp_filter())) {
923       olp_list_.push_back(MobileOperatorInfo::OnlinePortal {
924             raw_olp.url(),
925             (raw_olp.method() == raw_olp.GET) ? "GET" : "POST",
926             raw_olp.post_data()});
927     }
928   }
929   if (!user_olp_empty_) {
930     bool append_user_olp = true;
931     for (const auto& olp : olp_list_) {
932       append_user_olp &= (olp.url != user_olp_.url ||
933                           olp.method != user_olp_.method ||
934                           olp.post_data != user_olp_.post_data);
935     }
936     if (append_user_olp) {
937       olp_list_.push_back(user_olp_);
938     }
939   }
940 }
941 
PostNotifyOperatorChanged()942 void MobileOperatorInfoImpl::PostNotifyOperatorChanged() {
943   SLOG(this, 3) << __func__;
944   // If there was an outstanding task, it will get replaced.
945   notify_operator_changed_task_.Reset(
946       Bind(&MobileOperatorInfoImpl::NotifyOperatorChanged,
947            weak_ptr_factory_.GetWeakPtr()));
948   dispatcher_->PostTask(notify_operator_changed_task_.callback());
949 }
950 
NotifyOperatorChanged()951 void MobileOperatorInfoImpl::NotifyOperatorChanged() {
952   FOR_EACH_OBSERVER(MobileOperatorInfo::Observer,
953                     observers_,
954                     OnOperatorChanged());
955 }
956 
ShouldNotifyPropertyUpdate() const957 bool MobileOperatorInfoImpl::ShouldNotifyPropertyUpdate() const {
958   return IsMobileNetworkOperatorKnown() ||
959          IsMobileVirtualNetworkOperatorKnown();
960 }
961 
NormalizeOperatorName(const string & name) const962 string MobileOperatorInfoImpl::NormalizeOperatorName(const string& name) const {
963   string result = base::ToLowerASCII(name);
964   base::RemoveChars(result, base::kWhitespaceASCII, &result);
965   return result;
966 }
967 
968 }  // namespace shill
969