1 /*
2 * Copyright (C) 2024 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 use crate::utils::{
18 copy_file, copy_file_without_fsync, get_files_digest, read_pb_from_file, remove_file,
19 write_pb_to_file,
20 };
21 use crate::AconfigdError;
22 use aconfig_storage_file::{
23 list_flags, list_flags_with_info, FlagInfoBit, FlagValueSummary, FlagValueType,
24 };
25 use aconfig_storage_read_api::{
26 get_boolean_flag_value, get_flag_read_context, get_package_read_context,
27 get_storage_file_version, map_file,
28 };
29 use aconfig_storage_write_api::{
30 map_mutable_storage_file, set_boolean_flag_value, set_flag_has_local_override,
31 set_flag_has_server_override,
32 };
33 use aconfigd_protos::{ProtoFlagOverride, ProtoLocalFlagOverrides, ProtoPersistStorageRecord};
34 use anyhow::anyhow;
35 use log::debug;
36 use memmap2::{Mmap, MmapMut};
37 use std::collections::HashMap;
38 use std::path::{Path, PathBuf};
39
40 // In memory data structure for storage file locations for each container
41 #[derive(PartialEq, Debug, Clone)]
42 pub(crate) struct StorageRecord {
43 pub version: u32,
44 pub container: String, // container name
45 pub default_package_map: PathBuf, // default package map file
46 pub default_flag_map: PathBuf, // default flag map file
47 pub default_flag_val: PathBuf, // default flag val file
48 pub default_flag_info: PathBuf, // default flag info file
49 pub persist_package_map: PathBuf, // persist package.map file
50 pub persist_flag_map: PathBuf, // persist flag.map file
51 pub persist_flag_val: PathBuf, // persist flag.val file
52 pub persist_flag_info: PathBuf, // persist flag.info file
53 pub local_overrides: PathBuf, // local overrides pb file
54 pub boot_flag_val: PathBuf, // boot flag.val file
55 pub boot_flag_info: PathBuf, // boot flag.info file
56 pub digest: String, // hash for all default storage files
57 }
58
59 // Storage files for a particular container
60 #[derive(Debug)]
61 pub(crate) struct StorageFiles {
62 pub storage_record: StorageRecord,
63 pub package_map: Option<Mmap>,
64 pub flag_map: Option<Mmap>,
65 pub flag_val: Option<Mmap>, // default flag value file
66 pub boot_flag_val: Option<Mmap>, // boot flag value file
67 pub boot_flag_info: Option<Mmap>, // boot flag info file
68 pub persist_flag_val: Option<MmapMut>, // persist flag value file
69 pub persist_flag_info: Option<MmapMut>, // persist flag info file
70 pub mutable_boot_flag_val: Option<MmapMut>, // mutable boot flag value file
71 pub mutable_boot_flag_info: Option<MmapMut>, // mutable boot flag info file
72 }
73
74 // Compare two options of mmap/mmapmut
same_mmap_contents<T: std::ops::Deref<Target = [u8]>>( opt_a: &Option<T>, opt_b: &Option<T>, ) -> bool75 fn same_mmap_contents<T: std::ops::Deref<Target = [u8]>>(
76 opt_a: &Option<T>,
77 opt_b: &Option<T>,
78 ) -> bool {
79 match (opt_a, opt_b) {
80 (Some(map_a), Some(map_b)) => map_a[..] == map_b[..],
81 (None, None) => true,
82 _ => false,
83 }
84 }
85
86 impl PartialEq for StorageFiles {
eq(&self, other: &Self) -> bool87 fn eq(&self, other: &Self) -> bool {
88 self.storage_record == other.storage_record
89 && same_mmap_contents(&self.package_map, &other.package_map)
90 && same_mmap_contents(&self.flag_map, &other.flag_map)
91 && same_mmap_contents(&self.flag_val, &other.flag_val)
92 && same_mmap_contents(&self.boot_flag_val, &other.boot_flag_val)
93 && same_mmap_contents(&self.boot_flag_info, &other.boot_flag_info)
94 && same_mmap_contents(&self.persist_flag_val, &other.persist_flag_val)
95 && same_mmap_contents(&self.persist_flag_info, &other.persist_flag_info)
96 && same_mmap_contents(&self.mutable_boot_flag_val, &other.mutable_boot_flag_val)
97 && same_mmap_contents(&self.mutable_boot_flag_info, &other.mutable_boot_flag_info)
98 }
99 }
100
101 // Package and flag query context
102 #[derive(PartialEq, Debug)]
103 pub(crate) struct PackageFlagContext {
104 pub package: String,
105 pub flag: String,
106 pub package_exists: bool,
107 pub flag_exists: bool,
108 pub value_type: FlagValueType,
109 pub flag_index: u32,
110 }
111
112 // Flag snapshot in storage
113 #[derive(PartialEq, Debug)]
114 pub(crate) struct FlagSnapshot {
115 pub container: String,
116 pub package: String,
117 pub flag: String,
118 pub server_value: String,
119 pub local_value: String,
120 pub boot_value: String,
121 pub default_value: String,
122 pub is_readwrite: bool,
123 pub has_server_override: bool,
124 pub has_local_override: bool,
125 pub has_boot_local_override: bool,
126 }
127
128 impl StorageFiles {
129 /// Constructor from a container
from_container( container: &str, package_map: &Path, flag_map: &Path, flag_val: &Path, flag_info: &Path, root_dir: &Path, ) -> Result<Self, AconfigdError>130 pub(crate) fn from_container(
131 container: &str,
132 package_map: &Path,
133 flag_map: &Path,
134 flag_val: &Path,
135 flag_info: &Path,
136 root_dir: &Path,
137 ) -> Result<Self, AconfigdError> {
138 debug!("create storage files object from container {}", container);
139 let version =
140 get_storage_file_version(&flag_val.display().to_string()).map_err(|errmsg| {
141 AconfigdError::FailToGetFileVersion { file: flag_val.display().to_string(), errmsg }
142 })?;
143
144 let record = StorageRecord {
145 version,
146 container: container.to_string(),
147 default_package_map: package_map.to_path_buf(),
148 default_flag_map: flag_map.to_path_buf(),
149 default_flag_val: flag_val.to_path_buf(),
150 default_flag_info: flag_info.to_path_buf(),
151 persist_package_map: root_dir.join("maps").join(container.to_string() + ".package.map"),
152 persist_flag_map: root_dir.join("maps").join(container.to_string() + ".flag.map"),
153 persist_flag_val: root_dir.join("flags").join(container.to_string() + ".val"),
154 persist_flag_info: root_dir.join("flags").join(container.to_string() + ".info"),
155 local_overrides: root_dir
156 .join("flags")
157 .join(container.to_string() + "_local_overrides.pb"),
158 boot_flag_val: root_dir.join("boot").join(container.to_string() + ".val"),
159 boot_flag_info: root_dir.join("boot").join(container.to_string() + ".info"),
160 digest: get_files_digest(&[package_map, flag_map, flag_val, flag_info][..])?,
161 };
162
163 debug!("copy {} storage files to persist and boot directories", container);
164 copy_file(package_map, &record.persist_package_map, 0o444)?;
165 copy_file(flag_map, &record.persist_flag_map, 0o444)?;
166 copy_file(flag_val, &record.persist_flag_val, 0o644)?;
167 copy_file(flag_info, &record.persist_flag_info, 0o644)?;
168 copy_file(flag_val, &record.boot_flag_val, 0o644)?;
169 copy_file(flag_info, &record.boot_flag_info, 0o644)?;
170
171 let pb = ProtoLocalFlagOverrides::new();
172 write_pb_to_file::<ProtoLocalFlagOverrides>(&pb, &record.local_overrides)?;
173
174 let files = Self {
175 storage_record: record,
176 package_map: None,
177 flag_map: None,
178 flag_val: None,
179 boot_flag_val: None,
180 boot_flag_info: None,
181 persist_flag_val: None,
182 persist_flag_info: None,
183 mutable_boot_flag_val: None,
184 mutable_boot_flag_info: None,
185 };
186
187 Ok(files)
188 }
189
190 /// Constructor from a pb record
from_pb( pb: &ProtoPersistStorageRecord, root_dir: &Path, ) -> Result<Self, AconfigdError>191 pub(crate) fn from_pb(
192 pb: &ProtoPersistStorageRecord,
193 root_dir: &Path,
194 ) -> Result<Self, AconfigdError> {
195 debug!("create {} storage files object from pb entry", pb.container());
196 let record = StorageRecord {
197 version: pb.version(),
198 container: pb.container().to_string(),
199 default_package_map: PathBuf::from(pb.package_map()),
200 default_flag_map: PathBuf::from(pb.flag_map()),
201 default_flag_val: PathBuf::from(pb.flag_val()),
202 default_flag_info: PathBuf::from(pb.flag_info()),
203 persist_package_map: root_dir
204 .join("maps")
205 .join(pb.container().to_string() + ".package.map"),
206 persist_flag_map: root_dir.join("maps").join(pb.container().to_string() + ".flag.map"),
207 persist_flag_val: root_dir.join("flags").join(pb.container().to_string() + ".val"),
208 persist_flag_info: root_dir.join("flags").join(pb.container().to_string() + ".info"),
209 local_overrides: root_dir
210 .join("flags")
211 .join(pb.container().to_string() + "_local_overrides.pb"),
212 boot_flag_val: root_dir.join("boot").join(pb.container().to_string() + ".val"),
213 boot_flag_info: root_dir.join("boot").join(pb.container().to_string() + ".info"),
214 digest: pb.digest().to_string(),
215 };
216
217 Ok(Self {
218 storage_record: record,
219 package_map: None,
220 flag_map: None,
221 flag_val: None,
222 boot_flag_val: None,
223 boot_flag_info: None,
224 persist_flag_val: None,
225 persist_flag_info: None,
226 mutable_boot_flag_val: None,
227 mutable_boot_flag_info: None,
228 })
229 }
230
231 /// Get immutable file mapping of a file.
232 ///
233 /// # Safety
234 ///
235 /// The memory mapped file may have undefined behavior if there are writes to the underlying
236 /// file after being mapped. Ensure no writes can happen to the underlying file that is memory
237 /// mapped while this mapping stays alive to guarantee safety.
get_immutable_file_mapping(file_path: &Path) -> Result<Mmap, AconfigdError>238 unsafe fn get_immutable_file_mapping(file_path: &Path) -> Result<Mmap, AconfigdError> {
239 // SAFETY: As per the safety comment, there are no other writes to the underlying file.
240 unsafe {
241 map_file(&file_path.display().to_string()).map_err(|errmsg| {
242 AconfigdError::FailToMapFile { file: file_path.display().to_string(), errmsg }
243 })
244 }
245 }
246
247 /// Get package map memory mapping.
get_package_map(&mut self) -> Result<&Mmap, AconfigdError>248 fn get_package_map(&mut self) -> Result<&Mmap, AconfigdError> {
249 if self.package_map.is_none() {
250 // SAFETY: Here it is safe as package map files are always read only.
251 self.package_map = unsafe {
252 Some(Self::get_immutable_file_mapping(&self.storage_record.persist_package_map)?)
253 };
254 }
255 Ok(self.package_map.as_ref().unwrap())
256 }
257
258 /// Get flag map memory mapping.
get_flag_map(&mut self) -> Result<&Mmap, AconfigdError>259 fn get_flag_map(&mut self) -> Result<&Mmap, AconfigdError> {
260 if self.flag_map.is_none() {
261 // SAFETY: Here it is safe as flag map files are always read only.
262 self.flag_map = unsafe {
263 Some(Self::get_immutable_file_mapping(&self.storage_record.persist_flag_map)?)
264 };
265 }
266 Ok(self.flag_map.as_ref().unwrap())
267 }
268
269 /// Get default flag value memory mapping.
get_flag_val(&mut self) -> Result<&Mmap, AconfigdError>270 fn get_flag_val(&mut self) -> Result<&Mmap, AconfigdError> {
271 if self.flag_val.is_none() {
272 // SAFETY: Here it is safe as default flag value files are always read only.
273 self.flag_val = unsafe {
274 Some(Self::get_immutable_file_mapping(&self.storage_record.default_flag_val)?)
275 };
276 }
277 Ok(self.flag_val.as_ref().unwrap())
278 }
279
280 /// Get boot flag value memory mapping.
281 ///
282 /// # Safety
283 ///
284 /// The memory mapped file may have undefined behavior if there are writes to the underlying
285 /// file after being mapped. Ensure no writes can happen to the underlying file that is memory
286 /// mapped while this mapping stays alive to guarantee safety.
get_boot_flag_val(&mut self) -> Result<&Mmap, AconfigdError>287 unsafe fn get_boot_flag_val(&mut self) -> Result<&Mmap, AconfigdError> {
288 if self.boot_flag_val.is_none() {
289 // SAFETY: As per the safety comment, there are no other writes to the underlying file.
290 self.boot_flag_val = unsafe {
291 Some(Self::get_immutable_file_mapping(&self.storage_record.boot_flag_val)?)
292 };
293 }
294 Ok(self.boot_flag_val.as_ref().unwrap())
295 }
296
297 /// Get boot flag info memory mapping.
298 ///
299 /// # Safety
300 ///
301 /// The memory mapped file may have undefined behavior if there are writes to the underlying
302 /// file after being mapped. Ensure no writes can happen to the underlying file that is memory
303 /// mapped while this mapping stays alive to guarantee safety.
get_boot_flag_info(&mut self) -> Result<&Mmap, AconfigdError>304 unsafe fn get_boot_flag_info(&mut self) -> Result<&Mmap, AconfigdError> {
305 if self.boot_flag_info.is_none() {
306 // SAFETY: As per the safety comment, there are no other writes to the underlying file.
307 self.boot_flag_info = unsafe {
308 Some(Self::get_immutable_file_mapping(&self.storage_record.boot_flag_info)?)
309 };
310 }
311 Ok(self.boot_flag_info.as_ref().unwrap())
312 }
313
314 /// Get mutable file mapping of a file.
315 ///
316 /// # Safety
317 ///
318 /// The memory mapped file may have undefined behavior if there are writes to this
319 /// file not thru this memory mapped file or there are concurrent writes to this
320 /// memory mapped file. Ensure all writes to the underlying file are thru this memory
321 /// mapped file and there are no concurrent writes.
get_mutable_file_mapping( file_path: &Path, ) -> Result<MmapMut, AconfigdError>322 pub(crate) unsafe fn get_mutable_file_mapping(
323 file_path: &Path,
324 ) -> Result<MmapMut, AconfigdError> {
325 // SAFETY: As per the safety comment, there are no other writes to the underlying file.
326 unsafe {
327 map_mutable_storage_file(&file_path.display().to_string()).map_err(|errmsg| {
328 AconfigdError::FailToMapFile { file: file_path.display().to_string(), errmsg }
329 })
330 }
331 }
332
333 /// Get persist flag value memory mapping.
get_persist_flag_val(&mut self) -> Result<&mut MmapMut, AconfigdError>334 fn get_persist_flag_val(&mut self) -> Result<&mut MmapMut, AconfigdError> {
335 if self.persist_flag_val.is_none() {
336 // SAFETY: safety is ensured that all writes to the persist file is thru this
337 // memory mapping, and there are no concurrent writes
338 self.persist_flag_val = unsafe {
339 Some(Self::get_mutable_file_mapping(&self.storage_record.persist_flag_val)?)
340 };
341 }
342 Ok(self.persist_flag_val.as_mut().unwrap())
343 }
344
345 /// Get persist flag info memory mapping.
get_persist_flag_info(&mut self) -> Result<&mut MmapMut, AconfigdError>346 fn get_persist_flag_info(&mut self) -> Result<&mut MmapMut, AconfigdError> {
347 if self.persist_flag_info.is_none() {
348 // SAFETY: safety is ensured that all writes to the persist file is thru this
349 // memory mapping, and there are no concurrent writes
350 self.persist_flag_info = unsafe {
351 Some(Self::get_mutable_file_mapping(&self.storage_record.persist_flag_info)?)
352 };
353 }
354 Ok(self.persist_flag_info.as_mut().unwrap())
355 }
356
357 /// Get mutable boot flag value memory mapping.
get_mutable_boot_flag_val(&mut self) -> Result<&mut MmapMut, AconfigdError>358 fn get_mutable_boot_flag_val(&mut self) -> Result<&mut MmapMut, AconfigdError> {
359 if self.mutable_boot_flag_val.is_none() {
360 // SAFETY: safety is ensured that all writes to the persist file is thru this
361 // memory mapping, and there are no concurrent writes
362 self.mutable_boot_flag_val = unsafe {
363 Some(Self::get_mutable_file_mapping(&self.storage_record.boot_flag_val)?)
364 };
365 }
366 Ok(self.mutable_boot_flag_val.as_mut().unwrap())
367 }
368
369 /// Get mutable boot flag info memory mapping.
get_mutable_boot_flag_info(&mut self) -> Result<&mut MmapMut, AconfigdError>370 fn get_mutable_boot_flag_info(&mut self) -> Result<&mut MmapMut, AconfigdError> {
371 if self.mutable_boot_flag_info.is_none() {
372 // SAFETY: safety is ensured that all writes to the persist file is thru this
373 // memory mapping, and there are no concurrent writes
374 self.mutable_boot_flag_info = unsafe {
375 Some(Self::get_mutable_file_mapping(&self.storage_record.boot_flag_info)?)
376 };
377 }
378 Ok(self.mutable_boot_flag_info.as_mut().unwrap())
379 }
380
381 /// Get package and flag query context
get_package_flag_context( &mut self, package: &str, flag: &str, ) -> Result<PackageFlagContext, AconfigdError>382 pub(crate) fn get_package_flag_context(
383 &mut self,
384 package: &str,
385 flag: &str,
386 ) -> Result<PackageFlagContext, AconfigdError> {
387 let mut context = PackageFlagContext {
388 package: package.to_string(),
389 flag: flag.to_string(),
390 package_exists: false,
391 flag_exists: false,
392 value_type: FlagValueType::Boolean,
393 flag_index: 0,
394 };
395
396 if package.is_empty() {
397 return Ok(context);
398 }
399
400 let package_context =
401 get_package_read_context(self.get_package_map()?, package).map_err(|errmsg| {
402 AconfigdError::FailToGetPackageContext { package: package.to_string(), errmsg }
403 })?;
404
405 if let Some(pkg) = package_context {
406 context.package_exists = true;
407 if flag.is_empty() {
408 return Ok(context);
409 }
410
411 let flag_context = get_flag_read_context(self.get_flag_map()?, pkg.package_id, flag)
412 .map_err(|errmsg| AconfigdError::FailToGetFlagContext {
413 flag: package.to_string() + "." + flag,
414 errmsg,
415 })?;
416
417 if let Some(flg) = flag_context {
418 context.flag_exists = true;
419 context.value_type = FlagValueType::try_from(flg.flag_type).map_err(|errmsg| {
420 AconfigdError::InvalidFlagValueType {
421 flag: package.to_string() + "." + flag,
422 errmsg,
423 }
424 })?;
425 context.flag_index = pkg.boolean_start_index + flg.flag_index as u32;
426 }
427 } else {
428 debug!(
429 "failed to find package {} in container {}",
430 package, self.storage_record.container
431 );
432 }
433
434 Ok(context)
435 }
436
437 /// Check if has an aconfig package
has_package(&mut self, package: &str) -> Result<bool, AconfigdError>438 pub(crate) fn has_package(&mut self, package: &str) -> Result<bool, AconfigdError> {
439 let context = self.get_package_flag_context(package, "")?;
440 Ok(context.package_exists)
441 }
442
443 /// Get flag attribute bitfield
get_flag_attribute( &mut self, context: &PackageFlagContext, ) -> Result<u8, AconfigdError>444 pub(crate) fn get_flag_attribute(
445 &mut self,
446 context: &PackageFlagContext,
447 ) -> Result<u8, AconfigdError> {
448 if !context.flag_exists {
449 return Err(AconfigdError::FlagDoesNotExist {
450 flag: context.package.to_string() + "." + &context.flag,
451 });
452 }
453
454 let flag_info_file = self.get_persist_flag_info()?;
455 Ok(aconfig_storage_read_api::get_flag_attribute(
456 flag_info_file,
457 context.value_type,
458 context.flag_index,
459 )
460 .map_err(|errmsg| AconfigdError::FailToGetFlagAttribute {
461 flag: context.package.to_string() + "." + &context.flag,
462 errmsg,
463 })?)
464 }
465
466 /// Get flag value from a mapped file
get_flag_value_from_file( file: &[u8], context: &PackageFlagContext, ) -> Result<String, AconfigdError>467 fn get_flag_value_from_file(
468 file: &[u8],
469 context: &PackageFlagContext,
470 ) -> Result<String, AconfigdError> {
471 if !context.flag_exists {
472 return Err(AconfigdError::FlagDoesNotExist {
473 flag: context.package.to_string() + "." + &context.flag,
474 });
475 }
476
477 match context.value_type {
478 FlagValueType::Boolean => {
479 let value = get_boolean_flag_value(file, context.flag_index).map_err(|errmsg| {
480 AconfigdError::FailToGetFlagValue {
481 flag: context.package.to_string() + "." + &context.flag,
482 errmsg,
483 }
484 })?;
485 if value {
486 Ok(String::from("true"))
487 } else {
488 Ok(String::from("false"))
489 }
490 }
491 }
492 }
493
494 /// Get server flag value
get_server_flag_value( &mut self, context: &PackageFlagContext, ) -> Result<String, AconfigdError>495 pub(crate) fn get_server_flag_value(
496 &mut self,
497 context: &PackageFlagContext,
498 ) -> Result<String, AconfigdError> {
499 let attribute = self.get_flag_attribute(context)?;
500 if (attribute & FlagInfoBit::HasServerOverride as u8) == 0 {
501 return Ok(String::new());
502 }
503
504 let flag_val_file = self.get_persist_flag_val()?;
505 Self::get_flag_value_from_file(flag_val_file, context)
506 }
507
508 /// Get boot flag value
get_boot_flag_value( &mut self, context: &PackageFlagContext, ) -> Result<String, AconfigdError>509 pub(crate) fn get_boot_flag_value(
510 &mut self,
511 context: &PackageFlagContext,
512 ) -> Result<String, AconfigdError> {
513 // SAFETY: safety is ensured as we are only read from the memory mapping
514 let flag_val_file = unsafe { self.get_boot_flag_val()? };
515 Self::get_flag_value_from_file(flag_val_file, context)
516 }
517
518 /// Get default flag value
get_default_flag_value( &mut self, context: &PackageFlagContext, ) -> Result<String, AconfigdError>519 pub(crate) fn get_default_flag_value(
520 &mut self,
521 context: &PackageFlagContext,
522 ) -> Result<String, AconfigdError> {
523 let flag_val_file = self.get_flag_val()?;
524 Self::get_flag_value_from_file(flag_val_file, context)
525 }
526
527 /// Get local flag value
get_local_flag_value( &mut self, context: &PackageFlagContext, ) -> Result<String, AconfigdError>528 pub(crate) fn get_local_flag_value(
529 &mut self,
530 context: &PackageFlagContext,
531 ) -> Result<String, AconfigdError> {
532 let attribute = self.get_flag_attribute(context)?;
533 if (attribute & FlagInfoBit::HasLocalOverride as u8) == 0 {
534 return Ok(String::new());
535 }
536
537 let pb =
538 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
539
540 for entry in pb.overrides {
541 if entry.package_name() == context.package && entry.flag_name() == context.flag {
542 return Ok(String::from(entry.flag_value()));
543 }
544 }
545
546 Err(AconfigdError::FlagHasNoLocalOverride {
547 flag: context.package.to_string() + "." + &context.flag,
548 })
549 }
550
551 /// Set flag value to file
set_flag_value_to_file( file: &mut MmapMut, context: &PackageFlagContext, value: &str, ) -> Result<(), AconfigdError>552 pub(crate) fn set_flag_value_to_file(
553 file: &mut MmapMut,
554 context: &PackageFlagContext,
555 value: &str,
556 ) -> Result<(), AconfigdError> {
557 match context.value_type {
558 FlagValueType::Boolean => {
559 if value != "true" && value != "false" {
560 return Err(AconfigdError::InvalidFlagValue {
561 flag: context.package.to_string() + "." + &context.flag,
562 value: value.to_string(),
563 });
564 }
565 set_boolean_flag_value(file, context.flag_index, value == "true").map_err(
566 |errmsg| AconfigdError::FailToSetFlagValue {
567 flag: context.package.to_string() + "." + &context.flag,
568 errmsg,
569 },
570 )?;
571 }
572 }
573
574 Ok(())
575 }
576
577 /// Set flag has server override to file
set_flag_has_server_override_to_file( file: &mut MmapMut, context: &PackageFlagContext, value: bool, ) -> Result<(), AconfigdError>578 fn set_flag_has_server_override_to_file(
579 file: &mut MmapMut,
580 context: &PackageFlagContext,
581 value: bool,
582 ) -> Result<(), AconfigdError> {
583 set_flag_has_server_override(file, context.value_type, context.flag_index, value).map_err(
584 |errmsg| AconfigdError::FailToSetFlagHasServerOverride {
585 flag: context.package.to_string() + "." + &context.flag,
586 errmsg,
587 },
588 )?;
589
590 Ok(())
591 }
592
593 /// Set flag has local override to file
set_flag_has_local_override_to_file( file: &mut MmapMut, context: &PackageFlagContext, value: bool, ) -> Result<(), AconfigdError>594 pub(crate) fn set_flag_has_local_override_to_file(
595 file: &mut MmapMut,
596 context: &PackageFlagContext,
597 value: bool,
598 ) -> Result<(), AconfigdError> {
599 set_flag_has_local_override(file, context.value_type, context.flag_index, value).map_err(
600 |errmsg| AconfigdError::FailToSetFlagHasLocalOverride {
601 flag: context.package.to_string() + "." + &context.flag,
602 errmsg,
603 },
604 )?;
605
606 Ok(())
607 }
608
609 /// Server override a flag
stage_server_override( &mut self, context: &PackageFlagContext, value: &str, ) -> Result<(), AconfigdError>610 pub(crate) fn stage_server_override(
611 &mut self,
612 context: &PackageFlagContext,
613 value: &str,
614 ) -> Result<(), AconfigdError> {
615 debug!(
616 "staging server override for flag {} with value {}",
617 context.package.to_string() + "." + &context.flag,
618 value
619 );
620 let attribute = self.get_flag_attribute(context)?;
621 if (attribute & FlagInfoBit::IsReadWrite as u8) == 0 {
622 return Err(AconfigdError::FlagIsReadOnly {
623 flag: context.package.to_string() + "." + &context.flag,
624 });
625 }
626
627 let flag_val_file = self.get_persist_flag_val()?;
628 Self::set_flag_value_to_file(flag_val_file, context, value)?;
629
630 let flag_info_file = self.get_persist_flag_info()?;
631 Self::set_flag_has_server_override_to_file(flag_info_file, context, true)?;
632
633 Ok(())
634 }
635
636 /// Stage local override of a flag
stage_local_override( &mut self, context: &PackageFlagContext, value: &str, ) -> Result<(), AconfigdError>637 pub(crate) fn stage_local_override(
638 &mut self,
639 context: &PackageFlagContext,
640 value: &str,
641 ) -> Result<(), AconfigdError> {
642 debug!(
643 "staging local override for flag {} with value {}",
644 context.package.to_string() + "." + &context.flag,
645 value
646 );
647 let attribute = self.get_flag_attribute(context)?;
648 if (attribute & FlagInfoBit::IsReadWrite as u8) == 0 {
649 return Err(AconfigdError::FlagIsReadOnly {
650 flag: context.package.to_string() + "." + &context.flag,
651 });
652 }
653
654 let mut exist = false;
655 let mut pb =
656 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
657 for entry in &mut pb.overrides {
658 if entry.package_name() == context.package && entry.flag_name() == context.flag {
659 entry.set_flag_value(String::from(value));
660 exist = true;
661 break;
662 }
663 }
664 if !exist {
665 let mut new_entry = ProtoFlagOverride::new();
666 new_entry.set_package_name(context.package.clone());
667 new_entry.set_flag_name(context.flag.clone());
668 new_entry.set_flag_value(String::from(value));
669 pb.overrides.push(new_entry);
670 }
671
672 write_pb_to_file::<ProtoLocalFlagOverrides>(&pb, &self.storage_record.local_overrides)?;
673
674 let flag_info_file = self.get_persist_flag_info()?;
675 Self::set_flag_has_local_override_to_file(flag_info_file, context, true)?;
676
677 Ok(())
678 }
679
680 /// Stage and apply local override of a flag
stage_and_apply_local_override( &mut self, context: &PackageFlagContext, value: &str, ) -> Result<(), AconfigdError>681 pub(crate) fn stage_and_apply_local_override(
682 &mut self,
683 context: &PackageFlagContext,
684 value: &str,
685 ) -> Result<(), AconfigdError> {
686 self.stage_local_override(&context, value)?;
687
688 debug!(
689 "apply local override for flag {} with value {}",
690 context.package.to_string() + "." + &context.flag,
691 value
692 );
693 let mut mut_boot_flag_val = self.get_mutable_boot_flag_val()?;
694 Self::set_flag_value_to_file(&mut mut_boot_flag_val, &context, value)?;
695 let mut mut_boot_flag_info = self.get_mutable_boot_flag_info()?;
696 Self::set_flag_has_local_override_to_file(&mut mut_boot_flag_info, &context, true)?;
697 Ok(())
698 }
699
700 /// Apply all staged local overrides
apply_staged_local_overrides(&mut self) -> Result<(), AconfigdError>701 fn apply_staged_local_overrides(&mut self) -> Result<(), AconfigdError> {
702 debug!("apply staged local overrides for container {}", &self.storage_record.container);
703 let pb =
704 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
705
706 for entry in pb.overrides {
707 let context = self.get_package_flag_context(entry.package_name(), entry.flag_name())?;
708 let mut flag_val_file = self.get_mutable_boot_flag_val()?;
709 Self::set_flag_value_to_file(&mut flag_val_file, &context, entry.flag_value())?;
710 }
711
712 Ok(())
713 }
714
715 /// Apply both server and local overrides
apply_all_staged_overrides(&mut self) -> Result<(), AconfigdError>716 pub(crate) fn apply_all_staged_overrides(&mut self) -> Result<(), AconfigdError> {
717 debug!("apply staged server overrides for container {}", &self.storage_record.container);
718 copy_file_without_fsync(
719 &self.storage_record.persist_flag_val,
720 &self.storage_record.boot_flag_val,
721 0o644,
722 )?;
723 copy_file_without_fsync(
724 &self.storage_record.persist_flag_info,
725 &self.storage_record.boot_flag_info,
726 0o644,
727 )?;
728 self.apply_staged_local_overrides()?;
729 Ok(())
730 }
731
732 /// Get all current server overrides
get_all_server_overrides( &mut self, ) -> Result<Vec<FlagValueSummary>, AconfigdError>733 pub(crate) fn get_all_server_overrides(
734 &mut self,
735 ) -> Result<Vec<FlagValueSummary>, AconfigdError> {
736 debug!("get all staged server overrides for container {}", &self.storage_record.container);
737 let listed_flags = list_flags_with_info(
738 &self.storage_record.persist_package_map.display().to_string(),
739 &self.storage_record.persist_flag_map.display().to_string(),
740 &self.storage_record.persist_flag_val.display().to_string(),
741 &self.storage_record.persist_flag_info.display().to_string(),
742 )
743 .map_err(|errmsg| AconfigdError::FailToListFlagsWithInfo {
744 container: self.storage_record.container.clone(),
745 errmsg,
746 })?;
747
748 Ok(listed_flags
749 .into_iter()
750 .filter(|f| f.has_server_override)
751 .map(|f| FlagValueSummary {
752 package_name: f.package_name,
753 flag_name: f.flag_name,
754 flag_value: f.flag_value,
755 value_type: f.value_type,
756 })
757 .collect())
758 }
759
760 /// Get all local overrides
get_all_local_overrides( &mut self, ) -> Result<Vec<ProtoFlagOverride>, AconfigdError>761 pub(crate) fn get_all_local_overrides(
762 &mut self,
763 ) -> Result<Vec<ProtoFlagOverride>, AconfigdError> {
764 debug!("get all staged local overrides for container {}", &self.storage_record.container);
765 let pb =
766 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
767 Ok(pb.overrides)
768 }
769
770 /// Remove a local flag override
remove_local_override( &mut self, context: &PackageFlagContext, immediate: bool, ) -> Result<(), AconfigdError>771 pub(crate) fn remove_local_override(
772 &mut self,
773 context: &PackageFlagContext,
774 immediate: bool,
775 ) -> Result<(), AconfigdError> {
776 debug!(
777 "remove local override for flag {}",
778 context.package.to_string() + "." + &context.flag
779 );
780 let attribute = self.get_flag_attribute(context)?;
781 if (attribute & FlagInfoBit::HasLocalOverride as u8) == 0 {
782 return Err(AconfigdError::FlagHasNoLocalOverride {
783 flag: context.package.to_string() + "." + &context.flag,
784 });
785 }
786
787 let mut pb =
788 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
789 pb.overrides = pb
790 .overrides
791 .into_iter()
792 .filter(|f| f.package_name() != context.package || f.flag_name() != context.flag)
793 .collect();
794 write_pb_to_file::<ProtoLocalFlagOverrides>(&pb, &self.storage_record.local_overrides)?;
795
796 let flag_info_file = self.get_persist_flag_info()?;
797 Self::set_flag_has_local_override_to_file(flag_info_file, context, false)?;
798
799 if configinfra_framework_flags_rust::enable_immediate_clear_override_bugfix() && immediate {
800 let value = if (attribute & FlagInfoBit::HasServerOverride as u8) == 1 {
801 self.get_server_flag_value(&context)?
802 } else {
803 self.get_default_flag_value(&context)?
804 };
805
806 let mut mut_boot_flag_val = self.get_mutable_boot_flag_val()?;
807 Self::set_flag_value_to_file(&mut mut_boot_flag_val, &context, &value)?;
808
809 let mut mut_boot_flag_info = self.get_mutable_boot_flag_info()?;
810 Self::set_flag_has_local_override_to_file(&mut mut_boot_flag_info, &context, false)?;
811 }
812
813 Ok(())
814 }
815
816 /// Remove all local flag overrides
remove_all_local_overrides(&mut self) -> Result<(), AconfigdError>817 pub(crate) fn remove_all_local_overrides(&mut self) -> Result<(), AconfigdError> {
818 debug!("remove all local overrides for container {}", &self.storage_record.container);
819 let pb =
820 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
821
822 for entry in pb.overrides {
823 let context = self.get_package_flag_context(entry.package_name(), entry.flag_name())?;
824 let attribute = self.get_flag_attribute(&context)?;
825 if (attribute & FlagInfoBit::HasLocalOverride as u8) == 0 {
826 return Err(AconfigdError::FlagHasNoLocalOverride {
827 flag: context.package.to_string() + "." + &context.flag,
828 });
829 }
830
831 let flag_info_file = self.get_persist_flag_info()?;
832 Self::set_flag_has_local_override_to_file(flag_info_file, &context, false)?;
833 }
834
835 write_pb_to_file::<ProtoLocalFlagOverrides>(
836 &ProtoLocalFlagOverrides::new(),
837 &self.storage_record.local_overrides,
838 )?;
839
840 Ok(())
841 }
842
843 /// Clean up, it cannot be implemented as the drop trait as it needs to return a Result
remove_persist_files(&mut self) -> Result<(), AconfigdError>844 pub(crate) fn remove_persist_files(&mut self) -> Result<(), AconfigdError> {
845 debug!(
846 "remove all persistent storage files for container {}",
847 &self.storage_record.container
848 );
849 remove_file(&self.storage_record.persist_package_map)?;
850 remove_file(&self.storage_record.persist_flag_map)?;
851 remove_file(&self.storage_record.persist_flag_val)?;
852 remove_file(&self.storage_record.persist_flag_info)?;
853 remove_file(&self.storage_record.local_overrides)
854 }
855
856 /// get flag snapshot
get_flag_snapshot( &mut self, package: &str, flag: &str, ) -> Result<Option<FlagSnapshot>, AconfigdError>857 pub(crate) fn get_flag_snapshot(
858 &mut self,
859 package: &str,
860 flag: &str,
861 ) -> Result<Option<FlagSnapshot>, AconfigdError> {
862 let context = self.get_package_flag_context(package, flag)?;
863 if !context.flag_exists {
864 return Ok(None);
865 }
866
867 let attribute = self.get_flag_attribute(&context)?;
868 let server_value = self.get_server_flag_value(&context)?;
869 let local_value = self.get_local_flag_value(&context)?;
870 let boot_value = self.get_boot_flag_value(&context)?;
871 let default_value = self.get_default_flag_value(&context)?;
872
873 Ok(Some(FlagSnapshot {
874 container: self.storage_record.container.clone(),
875 package: package.to_string(),
876 flag: flag.to_string(),
877 server_value,
878 local_value,
879 boot_value,
880 default_value,
881 is_readwrite: attribute & FlagInfoBit::IsReadWrite as u8 != 0,
882 has_server_override: attribute & FlagInfoBit::HasServerOverride as u8 != 0,
883 has_local_override: attribute & FlagInfoBit::HasLocalOverride as u8 != 0,
884 has_boot_local_override: false, // This is unsupported for get_flag_snapshot.
885 }))
886 }
887
888 /// list flags in a package
list_flags_in_package( &mut self, package: &str, ) -> Result<Vec<FlagSnapshot>, AconfigdError>889 pub(crate) fn list_flags_in_package(
890 &mut self,
891 package: &str,
892 ) -> Result<Vec<FlagSnapshot>, AconfigdError> {
893 debug!("list all flags in package {}", &package);
894 if !self.has_package(package)? {
895 return Ok(Vec::new());
896 }
897
898 let mut snapshots: Vec<_> = list_flags_with_info(
899 &self.storage_record.persist_package_map.display().to_string(),
900 &self.storage_record.persist_flag_map.display().to_string(),
901 &self.storage_record.persist_flag_val.display().to_string(),
902 &self.storage_record.persist_flag_info.display().to_string(),
903 )
904 .map_err(|errmsg| AconfigdError::FailToListFlagsWithInfo {
905 container: self.storage_record.container.clone(),
906 errmsg,
907 })?
908 .into_iter()
909 .filter(|f| f.package_name == package)
910 .map(|f| FlagSnapshot {
911 container: self.storage_record.container.clone(),
912 package: f.package_name.clone(),
913 flag: f.flag_name.clone(),
914 server_value: if f.has_server_override { f.flag_value.clone() } else { String::new() },
915 local_value: String::new(),
916 boot_value: String::new(),
917 default_value: String::new(),
918 is_readwrite: f.is_readwrite,
919 has_server_override: f.has_server_override,
920 has_local_override: f.has_local_override,
921 has_boot_local_override: false, // Placeholder; this is mutated and set below.
922 })
923 .collect();
924
925 let mut flag_index = HashMap::new();
926 for (i, f) in snapshots.iter().enumerate() {
927 flag_index.insert(f.package.clone() + "/" + &f.flag, i);
928 }
929
930 let mut flags: Vec<_> = list_flags_with_info(
931 &self.storage_record.persist_package_map.display().to_string(),
932 &self.storage_record.persist_flag_map.display().to_string(),
933 &self.storage_record.boot_flag_val.display().to_string(),
934 &self.storage_record.boot_flag_info.display().to_string(),
935 )
936 .map_err(|errmsg| AconfigdError::FailToListFlags {
937 container: self.storage_record.container.clone(),
938 errmsg,
939 })?
940 .into_iter()
941 .filter(|f| f.package_name == package)
942 .collect();
943
944 for f in flags.iter() {
945 let full_flag_name = f.package_name.clone() + "/" + &f.flag_name;
946 let index =
947 flag_index.get(&full_flag_name).ok_or(AconfigdError::InternalError(anyhow!(
948 "Flag {}.{} appears in boot files but not in persist fliles",
949 &f.package_name,
950 &f.flag_name,
951 )))?;
952 snapshots[*index].boot_value = f.flag_value.clone();
953 snapshots[*index].has_boot_local_override = f.has_local_override;
954 }
955
956 let flags: Vec<_> = list_flags(
957 &self.storage_record.persist_package_map.display().to_string(),
958 &self.storage_record.persist_flag_map.display().to_string(),
959 &self.storage_record.default_flag_val.display().to_string(),
960 )
961 .map_err(|errmsg| AconfigdError::FailToListFlags {
962 container: self.storage_record.container.clone(),
963 errmsg,
964 })?
965 .into_iter()
966 .filter(|f| f.package_name == package)
967 .collect();
968
969 for f in flags.iter() {
970 let full_flag_name = f.package_name.clone() + "/" + &f.flag_name;
971 let index =
972 flag_index.get(&full_flag_name).ok_or(AconfigdError::InternalError(anyhow!(
973 "Flag {}.{} appears in default files but not in persist fliles",
974 &f.package_name,
975 &f.flag_name,
976 )))?;
977 snapshots[*index].default_value = f.flag_value.clone();
978 }
979
980 let pb =
981 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
982
983 for entry in pb.overrides {
984 let full_flag_name = entry.package_name().to_string() + "/" + entry.flag_name();
985 if let Some(index) = flag_index.get(&full_flag_name) {
986 snapshots[*index].local_value = entry.flag_value().to_string();
987 }
988 }
989
990 Ok(snapshots)
991 }
992
993 /// list all flags in a container
list_all_flags(&mut self) -> Result<Vec<FlagSnapshot>, AconfigdError>994 pub(crate) fn list_all_flags(&mut self) -> Result<Vec<FlagSnapshot>, AconfigdError> {
995 debug!("list all flags in container {}", &self.storage_record.container);
996 let mut snapshots: Vec<_> = list_flags_with_info(
997 &self.storage_record.persist_package_map.display().to_string(),
998 &self.storage_record.persist_flag_map.display().to_string(),
999 &self.storage_record.persist_flag_val.display().to_string(),
1000 &self.storage_record.persist_flag_info.display().to_string(),
1001 )
1002 .map_err(|errmsg| AconfigdError::FailToListFlagsWithInfo {
1003 container: self.storage_record.container.clone(),
1004 errmsg,
1005 })?
1006 .into_iter()
1007 .map(|f| FlagSnapshot {
1008 container: self.storage_record.container.clone(),
1009 package: f.package_name.clone(),
1010 flag: f.flag_name.clone(),
1011 server_value: if f.has_server_override { f.flag_value.clone() } else { String::new() },
1012 local_value: String::new(),
1013 boot_value: String::new(),
1014 default_value: String::new(),
1015 is_readwrite: f.is_readwrite,
1016 has_server_override: f.has_server_override,
1017 has_local_override: f.has_local_override,
1018 has_boot_local_override: false, // Placeholder value; this is mutated and set below.
1019 })
1020 .collect();
1021
1022 let mut flag_index = HashMap::new();
1023 for (i, f) in snapshots.iter().enumerate() {
1024 flag_index.insert(f.package.clone() + "/" + &f.flag, i);
1025 }
1026
1027 let mut flags: Vec<_> = list_flags_with_info(
1028 &self.storage_record.persist_package_map.display().to_string(),
1029 &self.storage_record.persist_flag_map.display().to_string(),
1030 &self.storage_record.boot_flag_val.display().to_string(),
1031 &self.storage_record.boot_flag_info.display().to_string(),
1032 )
1033 .map_err(|errmsg| AconfigdError::FailToListFlags {
1034 container: self.storage_record.container.clone(),
1035 errmsg,
1036 })?
1037 .into_iter()
1038 .collect();
1039
1040 for f in flags.iter() {
1041 let full_flag_name = f.package_name.clone() + "/" + &f.flag_name;
1042 let index =
1043 flag_index.get(&full_flag_name).ok_or(AconfigdError::InternalError(anyhow!(
1044 "Flag {}.{} appears in boot files but not in persist fliles",
1045 &f.package_name,
1046 &f.flag_name,
1047 )))?;
1048 snapshots[*index].boot_value = f.flag_value.clone();
1049 snapshots[*index].has_boot_local_override = f.has_local_override;
1050 }
1051
1052 let flags: Vec<_> = list_flags(
1053 &self.storage_record.persist_package_map.display().to_string(),
1054 &self.storage_record.persist_flag_map.display().to_string(),
1055 &self.storage_record.default_flag_val.display().to_string(),
1056 )
1057 .map_err(|errmsg| AconfigdError::FailToListFlags {
1058 container: self.storage_record.container.clone(),
1059 errmsg,
1060 })?
1061 .into_iter()
1062 .collect();
1063
1064 for f in flags.iter() {
1065 let full_flag_name = f.package_name.clone() + "/" + &f.flag_name;
1066 let index =
1067 flag_index.get(&full_flag_name).ok_or(AconfigdError::InternalError(anyhow!(
1068 "Flag {}.{} appears in default files but not in persist fliles",
1069 &f.package_name,
1070 &f.flag_name,
1071 )))?;
1072 snapshots[*index].default_value = f.flag_value.clone();
1073 }
1074
1075 let pb =
1076 read_pb_from_file::<ProtoLocalFlagOverrides>(&self.storage_record.local_overrides)?;
1077
1078 for entry in pb.overrides {
1079 let full_flag_name = entry.package_name().to_string() + "/" + entry.flag_name();
1080 if let Some(index) = flag_index.get(&full_flag_name) {
1081 snapshots[*index].local_value = entry.flag_value().to_string();
1082 }
1083 }
1084
1085 Ok(snapshots)
1086 }
1087 }
1088
1089 #[cfg(test)]
1090 mod tests {
1091 use super::*;
1092 use crate::test_utils::{has_same_content, ContainerMock, StorageRootDirMock};
1093 use aconfig_storage_file::StoredFlagType;
1094
create_mock_storage_files( container: &ContainerMock, root_dir: &StorageRootDirMock, ) -> StorageFiles1095 fn create_mock_storage_files(
1096 container: &ContainerMock,
1097 root_dir: &StorageRootDirMock,
1098 ) -> StorageFiles {
1099 StorageFiles::from_container(
1100 &container.name,
1101 &container.package_map,
1102 &container.flag_map,
1103 &container.flag_val,
1104 &container.flag_info,
1105 &root_dir.tmp_dir.path(),
1106 )
1107 .unwrap()
1108 }
1109
1110 #[test]
test_create_storage_file_from_container()1111 fn test_create_storage_file_from_container() {
1112 let container = ContainerMock::new();
1113 let root_dir = StorageRootDirMock::new();
1114 let storage_files = create_mock_storage_files(&container, &root_dir);
1115
1116 let expected_record = StorageRecord {
1117 version: 1,
1118 container: String::from("mockup"),
1119 default_package_map: container.package_map.clone(),
1120 default_flag_map: container.flag_map.clone(),
1121 default_flag_val: container.flag_val.clone(),
1122 default_flag_info: container.flag_info.clone(),
1123 persist_package_map: root_dir.maps_dir.join("mockup.package.map"),
1124 persist_flag_map: root_dir.maps_dir.join("mockup.flag.map"),
1125 persist_flag_val: root_dir.flags_dir.join("mockup.val"),
1126 persist_flag_info: root_dir.flags_dir.join("mockup.info"),
1127 local_overrides: root_dir.flags_dir.join("mockup_local_overrides.pb"),
1128 boot_flag_val: root_dir.boot_dir.join("mockup.val"),
1129 boot_flag_info: root_dir.boot_dir.join("mockup.info"),
1130 digest: get_files_digest(
1131 &[
1132 container.package_map.as_path(),
1133 container.flag_map.as_path(),
1134 container.flag_val.as_path(),
1135 container.flag_info.as_path(),
1136 ][..],
1137 )
1138 .unwrap(),
1139 };
1140
1141 let expected_storage_files = StorageFiles {
1142 storage_record: expected_record,
1143 package_map: None,
1144 flag_map: None,
1145 flag_val: None,
1146 boot_flag_val: None,
1147 boot_flag_info: None,
1148 persist_flag_val: None,
1149 persist_flag_info: None,
1150 mutable_boot_flag_val: None,
1151 mutable_boot_flag_info: None,
1152 };
1153
1154 assert_eq!(storage_files, expected_storage_files);
1155
1156 assert!(has_same_content(
1157 &container.package_map,
1158 &storage_files.storage_record.persist_package_map
1159 ));
1160 assert!(has_same_content(
1161 &container.flag_map,
1162 &storage_files.storage_record.persist_flag_map
1163 ));
1164 assert!(has_same_content(
1165 &container.flag_val,
1166 &storage_files.storage_record.persist_flag_val
1167 ));
1168 assert!(has_same_content(
1169 &container.flag_info,
1170 &storage_files.storage_record.persist_flag_info
1171 ));
1172 assert!(has_same_content(&container.flag_val, &storage_files.storage_record.boot_flag_val));
1173 assert!(has_same_content(
1174 &container.flag_info,
1175 &storage_files.storage_record.boot_flag_info
1176 ));
1177 assert!(storage_files.storage_record.local_overrides.exists());
1178 }
1179
1180 #[test]
test_create_storage_file_from_pb()1181 fn test_create_storage_file_from_pb() {
1182 let root_dir = StorageRootDirMock::new();
1183 let container = ContainerMock::new();
1184
1185 let persist_package_map = root_dir.maps_dir.join("mockup.package.map");
1186 let persist_flag_map = root_dir.maps_dir.join("mockup.flag.map");
1187 let persist_flag_val = root_dir.flags_dir.join("mockup.val");
1188 let persist_flag_info = root_dir.flags_dir.join("mockup.info");
1189 copy_file(&container.package_map, &persist_package_map, 0o444).unwrap();
1190 copy_file(&container.flag_map, &persist_flag_map, 0o444).unwrap();
1191 copy_file(&container.flag_val, &persist_flag_val, 0o644).unwrap();
1192 copy_file(&container.flag_info, &persist_flag_info, 0o644).unwrap();
1193
1194 let mut pb = ProtoPersistStorageRecord::new();
1195 pb.set_version(123);
1196 pb.set_container("mockup".to_string());
1197 pb.set_package_map(container.package_map.display().to_string());
1198 pb.set_flag_map(container.flag_map.display().to_string());
1199 pb.set_flag_val(container.flag_val.display().to_string());
1200 pb.set_flag_info(container.flag_info.display().to_string());
1201 pb.set_digest(String::from("abc"));
1202
1203 let storage_files = StorageFiles::from_pb(&pb, &root_dir.tmp_dir.path()).unwrap();
1204
1205 let expected_record = StorageRecord {
1206 version: 123,
1207 container: String::from("mockup"),
1208 default_package_map: container.package_map.clone(),
1209 default_flag_map: container.flag_map.clone(),
1210 default_flag_val: container.flag_val.clone(),
1211 default_flag_info: container.flag_info.clone(),
1212 persist_package_map: root_dir.maps_dir.join("mockup.package.map"),
1213 persist_flag_map: root_dir.maps_dir.join("mockup.flag.map"),
1214 persist_flag_val: root_dir.flags_dir.join("mockup.val"),
1215 persist_flag_info: root_dir.flags_dir.join("mockup.info"),
1216 local_overrides: root_dir.flags_dir.join("mockup_local_overrides.pb"),
1217 boot_flag_val: root_dir.boot_dir.join("mockup.val"),
1218 boot_flag_info: root_dir.boot_dir.join("mockup.info"),
1219 digest: String::from("abc"),
1220 };
1221
1222 let expected_storage_files = StorageFiles {
1223 storage_record: expected_record,
1224 package_map: None,
1225 flag_map: None,
1226 flag_val: None,
1227 boot_flag_val: None,
1228 boot_flag_info: None,
1229 persist_flag_val: None,
1230 persist_flag_info: None,
1231 mutable_boot_flag_val: None,
1232 mutable_boot_flag_info: None,
1233 };
1234
1235 assert_eq!(storage_files, expected_storage_files);
1236 }
1237
1238 #[test]
test_get_package_flag_context()1239 fn test_get_package_flag_context() {
1240 let container = ContainerMock::new();
1241 let root_dir = StorageRootDirMock::new();
1242 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1243
1244 let mut context = PackageFlagContext {
1245 package: String::from("not_exist"),
1246 flag: String::new(),
1247 package_exists: false,
1248 flag_exists: false,
1249 value_type: FlagValueType::Boolean,
1250 flag_index: 0,
1251 };
1252 let mut actual_context = storage_files.get_package_flag_context("not_exist", "").unwrap();
1253 assert_eq!(context, actual_context);
1254
1255 context.package = String::from("com.android.aconfig.storage.test_1");
1256 context.package_exists = true;
1257 actual_context = storage_files
1258 .get_package_flag_context("com.android.aconfig.storage.test_1", "")
1259 .unwrap();
1260 assert_eq!(context, actual_context);
1261
1262 context.flag = String::from("not_exist");
1263 actual_context = storage_files
1264 .get_package_flag_context("com.android.aconfig.storage.test_1", "not_exist")
1265 .unwrap();
1266 assert_eq!(context, actual_context);
1267
1268 context.flag = String::from("enabled_rw");
1269 context.flag_exists = true;
1270 context.flag_index = 2;
1271 actual_context = storage_files
1272 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1273 .unwrap();
1274 assert_eq!(context, actual_context);
1275
1276 context.package = String::from("com.android.aconfig.storage.test_2");
1277 context.flag = String::from("disabled_rw");
1278 context.flag_index = 3;
1279 actual_context = storage_files
1280 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1281 .unwrap();
1282 assert_eq!(context, actual_context);
1283 }
1284
1285 #[test]
test_has_package()1286 fn test_has_package() {
1287 let container = ContainerMock::new();
1288 let root_dir = StorageRootDirMock::new();
1289 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1290 assert!(!storage_files.has_package("not_exist").unwrap());
1291 assert!(storage_files.has_package("com.android.aconfig.storage.test_1").unwrap());
1292 }
1293
1294 #[test]
test_get_flag_attribute()1295 fn test_get_flag_attribute() {
1296 let container = ContainerMock::new();
1297 let root_dir = StorageRootDirMock::new();
1298 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1299 let mut context = storage_files
1300 .get_package_flag_context("com.android.aconfig.storage.test_1", "not_exist")
1301 .unwrap();
1302 assert!(storage_files.get_flag_attribute(&context).is_err());
1303
1304 context = storage_files
1305 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1306 .unwrap();
1307 let attribute = storage_files.get_flag_attribute(&context).unwrap();
1308 assert!(attribute & (FlagInfoBit::IsReadWrite as u8) != 0);
1309 assert!(attribute & (FlagInfoBit::HasServerOverride as u8) == 0);
1310 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) == 0);
1311 }
1312
1313 #[test]
test_get_server_flag_value()1314 fn test_get_server_flag_value() {
1315 let container = ContainerMock::new();
1316 let root_dir = StorageRootDirMock::new();
1317 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1318 let context = storage_files
1319 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1320 .unwrap();
1321
1322 assert_eq!(&storage_files.get_server_flag_value(&context).unwrap(), "");
1323 storage_files.stage_server_override(&context, "false").unwrap();
1324 assert_eq!(&storage_files.get_server_flag_value(&context).unwrap(), "false");
1325 storage_files.stage_server_override(&context, "true").unwrap();
1326 assert_eq!(&storage_files.get_server_flag_value(&context).unwrap(), "true");
1327 }
1328
1329 #[test]
test_get_boot_flag_value()1330 fn test_get_boot_flag_value() {
1331 let container = ContainerMock::new();
1332 let root_dir = StorageRootDirMock::new();
1333 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1334 let mut context = storage_files
1335 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1336 .unwrap();
1337 assert_eq!(storage_files.get_boot_flag_value(&context).unwrap(), "true");
1338 context = storage_files
1339 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1340 .unwrap();
1341 assert_eq!(storage_files.get_boot_flag_value(&context).unwrap(), "false");
1342 }
1343
1344 #[test]
test_get_default_flag_value()1345 fn test_get_default_flag_value() {
1346 let container = ContainerMock::new();
1347 let root_dir = StorageRootDirMock::new();
1348 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1349 let mut context = storage_files
1350 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1351 .unwrap();
1352 assert_eq!(storage_files.get_default_flag_value(&context).unwrap(), "true");
1353 context = storage_files
1354 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1355 .unwrap();
1356 assert_eq!(storage_files.get_default_flag_value(&context).unwrap(), "false");
1357 }
1358
1359 #[test]
test_get_local_flag_value()1360 fn test_get_local_flag_value() {
1361 let container = ContainerMock::new();
1362 let root_dir = StorageRootDirMock::new();
1363 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1364 let context = storage_files
1365 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1366 .unwrap();
1367 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "");
1368 storage_files.stage_local_override(&context, "false").unwrap();
1369 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "false");
1370 storage_files.stage_local_override(&context, "true").unwrap();
1371 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "true");
1372 }
1373
1374 #[test]
test_stage_server_override()1375 fn test_stage_server_override() {
1376 let container = ContainerMock::new();
1377 let root_dir = StorageRootDirMock::new();
1378 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1379 let context = storage_files
1380 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1381 .unwrap();
1382 storage_files.stage_server_override(&context, "false").unwrap();
1383 assert_eq!(&storage_files.get_server_flag_value(&context).unwrap(), "false");
1384 let attribute = storage_files.get_flag_attribute(&context).unwrap();
1385 assert!(attribute & (FlagInfoBit::HasServerOverride as u8) != 0);
1386 }
1387
1388 #[test]
test_stage_local_override()1389 fn test_stage_local_override() {
1390 let container = ContainerMock::new();
1391 let root_dir = StorageRootDirMock::new();
1392 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1393 let context = storage_files
1394 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1395 .unwrap();
1396 storage_files.stage_local_override(&context, "false").unwrap();
1397 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "false");
1398 let attribute = storage_files.get_flag_attribute(&context).unwrap();
1399 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) != 0);
1400 }
1401
1402 #[test]
test_stage_and_apply_local_override()1403 fn test_stage_and_apply_local_override() {
1404 let container = ContainerMock::new();
1405 let root_dir = StorageRootDirMock::new();
1406 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1407 let context = storage_files
1408 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1409 .unwrap();
1410 storage_files.stage_and_apply_local_override(&context, "false").unwrap();
1411 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "false");
1412 assert_eq!(&storage_files.get_boot_flag_value(&context).unwrap(), "false");
1413 let attribute = storage_files.get_flag_attribute(&context).unwrap();
1414 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) != 0);
1415 }
1416
1417 #[test]
test_apply_all_staged_overrides()1418 fn test_apply_all_staged_overrides() {
1419 let container = ContainerMock::new();
1420 let root_dir = StorageRootDirMock::new();
1421 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1422
1423 let context_one = storage_files
1424 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1425 .unwrap();
1426 storage_files.stage_server_override(&context_one, "false").unwrap();
1427
1428 let context_two = storage_files
1429 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1430 .unwrap();
1431 storage_files.stage_server_override(&context_two, "false").unwrap();
1432 storage_files.stage_local_override(&context_two, "true").unwrap();
1433
1434 storage_files.apply_all_staged_overrides().unwrap();
1435
1436 assert!(storage_files.storage_record.boot_flag_val.exists());
1437 assert!(storage_files.storage_record.boot_flag_info.exists());
1438
1439 assert_eq!(storage_files.get_boot_flag_value(&context_one).unwrap(), "false");
1440 assert_eq!(storage_files.get_boot_flag_value(&context_two).unwrap(), "true");
1441 }
1442
1443 #[test]
test_get_all_server_overrides()1444 fn test_get_all_server_overrides() {
1445 let container = ContainerMock::new();
1446 let root_dir = StorageRootDirMock::new();
1447 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1448 let mut context = storage_files
1449 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1450 .unwrap();
1451 storage_files.stage_server_override(&context, "false").unwrap();
1452 context = storage_files
1453 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1454 .unwrap();
1455 storage_files.stage_server_override(&context, "true").unwrap();
1456 let server_overrides = storage_files.get_all_server_overrides().unwrap();
1457 assert_eq!(server_overrides.len(), 2);
1458 assert_eq!(
1459 server_overrides[0],
1460 FlagValueSummary {
1461 package_name: "com.android.aconfig.storage.test_1".to_string(),
1462 flag_name: "enabled_rw".to_string(),
1463 flag_value: "false".to_string(),
1464 value_type: StoredFlagType::ReadWriteBoolean,
1465 }
1466 );
1467 assert_eq!(
1468 server_overrides[1],
1469 FlagValueSummary {
1470 package_name: "com.android.aconfig.storage.test_2".to_string(),
1471 flag_name: "disabled_rw".to_string(),
1472 flag_value: "true".to_string(),
1473 value_type: StoredFlagType::ReadWriteBoolean,
1474 }
1475 );
1476 }
1477
1478 #[test]
test_get_all_local_overrides()1479 fn test_get_all_local_overrides() {
1480 let container = ContainerMock::new();
1481 let root_dir = StorageRootDirMock::new();
1482 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1483
1484 let context_one = storage_files
1485 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1486 .unwrap();
1487 storage_files.stage_local_override(&context_one, "false").unwrap();
1488
1489 let context_two = storage_files
1490 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1491 .unwrap();
1492 storage_files.stage_local_override(&context_two, "false").unwrap();
1493
1494 let local_overrides = storage_files.get_all_local_overrides().unwrap();
1495 assert_eq!(local_overrides.len(), 2);
1496
1497 let mut override_proto = ProtoFlagOverride::new();
1498 override_proto.set_package_name("com.android.aconfig.storage.test_1".to_string());
1499 override_proto.set_flag_name("enabled_rw".to_string());
1500 override_proto.set_flag_value("false".to_string());
1501 assert_eq!(local_overrides[0], override_proto);
1502
1503 override_proto.set_package_name("com.android.aconfig.storage.test_2".to_string());
1504 override_proto.set_flag_name("disabled_rw".to_string());
1505 assert_eq!(local_overrides[1], override_proto);
1506 }
1507
1508 #[test]
test_remove_local_override()1509 fn test_remove_local_override() {
1510 let container = ContainerMock::new();
1511 let root_dir = StorageRootDirMock::new();
1512 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1513 let context = storage_files
1514 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1515 .unwrap();
1516
1517 assert!(storage_files.remove_local_override(&context, false).is_err());
1518 storage_files.stage_local_override(&context, "false").unwrap();
1519 storage_files.remove_local_override(&context, false).unwrap();
1520 assert_eq!(&storage_files.get_local_flag_value(&context).unwrap(), "");
1521 let attribute = storage_files.get_flag_attribute(&context).unwrap();
1522 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) == 0);
1523 }
1524
1525 #[test]
test_remove_all_local_overrides()1526 fn test_remove_all_local_overrides() {
1527 let container = ContainerMock::new();
1528 let root_dir = StorageRootDirMock::new();
1529 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1530
1531 let context_one = storage_files
1532 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1533 .unwrap();
1534 storage_files.stage_local_override(&context_one, "false").unwrap();
1535
1536 let context_two = storage_files
1537 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1538 .unwrap();
1539 storage_files.stage_local_override(&context_two, "false").unwrap();
1540
1541 let mut pb = read_pb_from_file::<ProtoLocalFlagOverrides>(
1542 &storage_files.storage_record.local_overrides,
1543 )
1544 .unwrap();
1545 assert_eq!(pb.overrides.len(), 2);
1546
1547 storage_files.remove_all_local_overrides().unwrap();
1548
1549 assert_eq!(&storage_files.get_local_flag_value(&context_one).unwrap(), "");
1550 let mut attribute = storage_files.get_flag_attribute(&context_one).unwrap();
1551 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) == 0);
1552
1553 assert_eq!(&storage_files.get_local_flag_value(&context_two).unwrap(), "");
1554 attribute = storage_files.get_flag_attribute(&context_one).unwrap();
1555 assert!(attribute & (FlagInfoBit::HasLocalOverride as u8) == 0);
1556
1557 pb = read_pb_from_file::<ProtoLocalFlagOverrides>(
1558 &storage_files.storage_record.local_overrides,
1559 )
1560 .unwrap();
1561 assert_eq!(pb.overrides.len(), 0);
1562 }
1563
1564 #[test]
test_remove_persist_files()1565 fn test_remove_persist_files() {
1566 let container = ContainerMock::new();
1567 let root_dir = StorageRootDirMock::new();
1568 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1569 write_pb_to_file::<ProtoLocalFlagOverrides>(
1570 &ProtoLocalFlagOverrides::new(),
1571 &storage_files.storage_record.local_overrides,
1572 )
1573 .unwrap();
1574 assert!(storage_files.storage_record.persist_package_map.exists());
1575 assert!(storage_files.storage_record.persist_flag_map.exists());
1576 assert!(storage_files.storage_record.persist_flag_val.exists());
1577 assert!(storage_files.storage_record.persist_flag_info.exists());
1578 assert!(storage_files.storage_record.local_overrides.exists());
1579
1580 storage_files.remove_persist_files().unwrap();
1581 assert!(!storage_files.storage_record.persist_package_map.exists());
1582 assert!(!storage_files.storage_record.persist_flag_map.exists());
1583 assert!(!storage_files.storage_record.persist_flag_val.exists());
1584 assert!(!storage_files.storage_record.persist_flag_info.exists());
1585 assert!(!storage_files.storage_record.local_overrides.exists());
1586 }
1587
1588 #[test]
test_get_flag_snapshot()1589 fn test_get_flag_snapshot() {
1590 let container = ContainerMock::new();
1591 let root_dir = StorageRootDirMock::new();
1592 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1593
1594 let mut flag = storage_files
1595 .get_flag_snapshot("com.android.aconfig.storage.test_1", "not_exist")
1596 .unwrap();
1597 assert_eq!(flag, None);
1598
1599 let context = storage_files
1600 .get_package_flag_context("com.android.aconfig.storage.test_1", "disabled_rw")
1601 .unwrap();
1602 storage_files.stage_server_override(&context, "false").unwrap();
1603 storage_files.stage_local_override(&context, "true").unwrap();
1604 storage_files.apply_all_staged_overrides().unwrap();
1605
1606 flag = storage_files
1607 .get_flag_snapshot("com.android.aconfig.storage.test_1", "disabled_rw")
1608 .unwrap();
1609
1610 let expected_flag = FlagSnapshot {
1611 container: String::from("mockup"),
1612 package: String::from("com.android.aconfig.storage.test_1"),
1613 flag: String::from("disabled_rw"),
1614 server_value: String::from("false"),
1615 local_value: String::from("true"),
1616 boot_value: String::from("true"),
1617 default_value: String::from("false"),
1618 is_readwrite: true,
1619 has_server_override: true,
1620 has_local_override: true,
1621 has_boot_local_override: false,
1622 };
1623
1624 assert_eq!(flag, Some(expected_flag));
1625 }
1626
1627 #[test]
test_list_flags_in_package()1628 fn test_list_flags_in_package() {
1629 let container = ContainerMock::new();
1630 let root_dir = StorageRootDirMock::new();
1631 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1632
1633 let context_one = storage_files
1634 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1635 .unwrap();
1636 storage_files.stage_server_override(&context_one, "false").unwrap();
1637 let context_two = storage_files
1638 .get_package_flag_context("com.android.aconfig.storage.test_1", "disabled_rw")
1639 .unwrap();
1640 storage_files.stage_server_override(&context_two, "false").unwrap();
1641 storage_files.stage_local_override(&context_two, "true").unwrap();
1642 storage_files.apply_all_staged_overrides().unwrap();
1643
1644 let flags =
1645 storage_files.list_flags_in_package("com.android.aconfig.storage.test_1").unwrap();
1646
1647 let mut flag = FlagSnapshot {
1648 container: String::from("mockup"),
1649 package: String::from("com.android.aconfig.storage.test_1"),
1650 flag: String::from("disabled_rw"),
1651 server_value: String::from("false"),
1652 local_value: String::from("true"),
1653 boot_value: String::from("true"),
1654 default_value: String::from("false"),
1655 is_readwrite: true,
1656 has_server_override: true,
1657 has_local_override: true,
1658 has_boot_local_override: false,
1659 };
1660 assert_eq!(flags[0], flag);
1661
1662 flag = FlagSnapshot {
1663 container: String::from("mockup"),
1664 package: String::from("com.android.aconfig.storage.test_1"),
1665 flag: String::from("enabled_ro"),
1666 server_value: String::new(),
1667 local_value: String::new(),
1668 boot_value: String::from("true"),
1669 default_value: String::from("true"),
1670 is_readwrite: false,
1671 has_server_override: false,
1672 has_local_override: false,
1673 has_boot_local_override: false,
1674 };
1675 assert_eq!(flags[1], flag);
1676
1677 flag = FlagSnapshot {
1678 container: String::from("mockup"),
1679 package: String::from("com.android.aconfig.storage.test_1"),
1680 flag: String::from("enabled_rw"),
1681 server_value: String::from("false"),
1682 local_value: String::new(),
1683 boot_value: String::from("false"),
1684 default_value: String::from("true"),
1685 is_readwrite: true,
1686 has_server_override: true,
1687 has_local_override: false,
1688 has_boot_local_override: false,
1689 };
1690 assert_eq!(flags[2], flag);
1691 }
1692
1693 #[test]
test_list_all_flags()1694 fn test_list_all_flags() {
1695 let container = ContainerMock::new();
1696 let root_dir = StorageRootDirMock::new();
1697 let mut storage_files = create_mock_storage_files(&container, &root_dir);
1698
1699 let context_one = storage_files
1700 .get_package_flag_context("com.android.aconfig.storage.test_1", "enabled_rw")
1701 .unwrap();
1702 storage_files.stage_server_override(&context_one, "false").unwrap();
1703 let context_two = storage_files
1704 .get_package_flag_context("com.android.aconfig.storage.test_2", "disabled_rw")
1705 .unwrap();
1706 storage_files.stage_server_override(&context_two, "false").unwrap();
1707 storage_files.stage_local_override(&context_two, "true").unwrap();
1708 storage_files.apply_all_staged_overrides().unwrap();
1709
1710 let flags = storage_files.list_all_flags().unwrap();
1711 assert_eq!(flags.len(), 8);
1712
1713 let mut flag = FlagSnapshot {
1714 container: String::from("mockup"),
1715 package: String::from("com.android.aconfig.storage.test_1"),
1716 flag: String::from("enabled_rw"),
1717 server_value: String::from("false"),
1718 local_value: String::new(),
1719 boot_value: String::from("false"),
1720 default_value: String::from("true"),
1721 is_readwrite: true,
1722 has_server_override: true,
1723 has_local_override: false,
1724 has_boot_local_override: false,
1725 };
1726 assert_eq!(flags[2], flag);
1727
1728 flag = FlagSnapshot {
1729 container: String::from("mockup"),
1730 package: String::from("com.android.aconfig.storage.test_2"),
1731 flag: String::from("disabled_rw"),
1732 server_value: String::from("false"),
1733 local_value: String::from("true"),
1734 boot_value: String::from("true"),
1735 default_value: String::from("false"),
1736 is_readwrite: true,
1737 has_server_override: true,
1738 has_local_override: true,
1739 has_boot_local_override: false,
1740 };
1741 assert_eq!(flags[3], flag);
1742 }
1743 }
1744