servo: Merge #17141 - Ged rid of libfontconfig in Android (from MortimerGoro:android_fonts); r=mbrubeck
authorImanol Fernandez <mortimergoro@gmail.com>
Tue, 06 Jun 2017 13:29:05 -0700
changeset 410872 e5060676549102c4659195d24df81c85c0dd1281
parent 410871 aaf5e23caa034838fcd04a5d983fca1cd8186b18
child 410873 9eec39879b33d9ad0e941d799fdf691e7fe6a689
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
bugs17141, 16195
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #17141 - Ged rid of libfontconfig in Android (from MortimerGoro:android_fonts); r=mbrubeck Libfontconfig dependency is causing huge startup times in Android (20 seconds on each page load!). On other platforms fontconfig caches are already available or can be prebuilt on installation scripts, but this can't be done on Android. Updating libfontconfig dependency doesn't fix the problem either. This PR gets rid of libfontconfig in Android. It queries available fonts and variations from Android System font configuration files. Android doesn't provide an API to query system fonts until Android O (which is very far from the minimum API right now...) --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #16195 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: f388c0ab1e4df8cbd94d58eb7657f36baaf813fe
servo/Cargo.lock
servo/components/gfx/Cargo.toml
servo/components/gfx/lib.rs
servo/components/gfx/platform/freetype/android/font_list.rs
servo/components/gfx/platform/freetype/font_list.rs
servo/components/gfx/platform/mod.rs
servo/ports/servo/main.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1060,16 +1060,17 @@ dependencies = [
  "style 0.0.1",
  "style_traits 0.0.1",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-bidi 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.40.0 (git+https://github.com/servo/webrender)",
  "xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml5ever 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gfx_tests"
 version = "0.0.1"
 dependencies = [
  "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx 0.0.1",
--- a/servo/components/gfx/Cargo.toml
+++ b/servo/components/gfx/Cargo.toml
@@ -45,16 +45,21 @@ xi-unicode = "0.1.0"
 [target.'cfg(target_os = "macos")'.dependencies]
 byteorder = "1.0"
 core-foundation = "0.3"
 core-graphics = "0.7"
 core-text = "4.0"
 
 [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
 freetype = "0.2"
+
+[target.'cfg(target_os = "linux")'.dependencies]
 servo-fontconfig = "0.2.1"
 
+[target.'cfg(target_os = "android")'.dependencies]
+xml5ever = {version = "0.7", features = ["unstable"]}
+
 [target.'cfg(any(target_feature = "sse2", target_feature = "neon"))'.dependencies]
 simd = "0.2.0"
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.3"
 truetype = "0.26"
--- a/servo/components/gfx/lib.rs
+++ b/servo/components/gfx/lib.rs
@@ -28,17 +28,17 @@ extern crate bitflags;
 
 // Windows-specific library dependencies
 #[cfg(target_os = "windows")] extern crate dwrote;
 #[cfg(target_os = "windows")] extern crate truetype;
 
 extern crate euclid;
 extern crate fnv;
 
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(target_os = "linux")]
 extern crate fontconfig;
 extern crate fontsan;
 #[cfg(any(target_os = "linux", target_os = "android"))]
 extern crate freetype;
 extern crate gfx_traits;
 
 // Eventually we would like the shaper to be pluggable, as many operating systems have their own
 // shapers. For now, however, this is a hard dependency.
@@ -68,16 +68,18 @@ extern crate simd;
 extern crate smallvec;
 extern crate style;
 extern crate style_traits;
 extern crate time;
 extern crate unicode_bidi;
 extern crate unicode_script;
 extern crate webrender_traits;
 extern crate xi_unicode;
+#[cfg(target_os = "android")]
+extern crate xml5ever;
 
 #[deny(unsafe_code)]
 pub mod display_list;
 
 // Fonts
 #[macro_use] pub mod font;
 pub mod font_cache_thread;
 pub mod font_context;
new file mode 100644
--- /dev/null
+++ b/servo/components/gfx/platform/freetype/android/font_list.rs
@@ -0,0 +1,481 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::RefCell;
+use std::fs::File;
+use std::io::{self, Read};
+use std::path::Path;
+use xml5ever::Attribute;
+use xml5ever::driver::parse_document;
+use xml5ever::rcdom::*;
+use xml5ever::rcdom::{Node, RcDom};
+use xml5ever::tendril::TendrilSink;
+
+lazy_static! {
+    static ref FONT_LIST: FontList = FontList::new();
+}
+
+// Android doesn't provide an API to query system fonts until Android O:
+// https://developer.android.com/reference/android/text/FontConfig.html
+// System font configuration files must be parsed until Android O version is set as the minimum target.
+// Android uses XML files to handle font mapping configurations.
+// On Android API 21+ font mappings are loaded from /etc/fonts.xml.
+// Each entry consists of a family with various font names, or a font alias.
+// Example:
+//   <familyset>
+//       <!-- first font is default -->
+//       <family name="sans-serif">
+//           <font weight="100" style="normal">Roboto-Thin.ttf</font>
+//           <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+//           <font weight="300" style="normal">Roboto-Light.ttf</font>
+//           <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
+//           <font weight="400" style="normal">Roboto-Regular.ttf</font>
+//           <font weight="400" style="italic">Roboto-Italic.ttf</font>
+//           <font weight="500" style="normal">Roboto-Medium.ttf</font>
+//           <font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
+//           <font weight="900" style="normal">Roboto-Black.ttf</font>
+//           <font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
+//           <font weight="700" style="normal">Roboto-Bold.ttf</font>
+//           <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
+//       </family>//
+
+//       <!-- Note that aliases must come after the fonts they reference. -->
+//       <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+//       <alias name="sans-serif-light" to="sans-serif" weight="300" />
+//       <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+//       <alias name="sans-serif-black" to="sans-serif" weight="900" />
+//       <alias name="arial" to="sans-serif" />
+//       <alias name="helvetica" to="sans-serif" />
+//       <alias name="tahoma" to="sans-serif" />
+//       <alias name="verdana" to="sans-serif" />
+
+//       <family name="sans-serif-condensed">
+//           <font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
+//           <font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
+//           <font weight="400" style="normal">RobotoCondensed-Regular.ttf</font>
+//           <font weight="400" style="italic">RobotoCondensed-Italic.ttf</font>
+//           <font weight="700" style="normal">RobotoCondensed-Bold.ttf</font>
+//           <font weight="700" style="italic">RobotoCondensed-BoldItalic.ttf</font>
+//       </family>
+//       <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+//   </familyset>
+// On Android API 17-20 font mappings are loaded from /system/etc/system_fonts.xml
+// Each entry consists of a family with a nameset and a fileset.
+// Example:
+//  <familyset>
+//      <family>
+//          <nameset>
+//              <name>sans-serif</name>
+//              <name>arial</name>
+//              <name>helvetica</name>
+//              <name>tahoma</name>
+//              <name>verdana</name>
+//          </nameset>
+//          <fileset>
+//              <file>Roboto-Regular.ttf</file>
+//              <file>Roboto-Bold.ttf</file>
+//              <file>Roboto-Italic.ttf</file>
+//              <file>Roboto-BoldItalic.ttf</file>
+//          </fileset>
+//      </family>//
+
+//      <family>
+//          <nameset>
+//              <name>sans-serif-light</name>
+//          </nameset>
+//          <fileset>
+//              <file>Roboto-Light.ttf</file>
+//              <file>Roboto-LightItalic.ttf</file>
+//          </fileset>
+//      </family>//
+
+//      <family>
+//          <nameset>
+//              <name>sans-serif-thin</name>
+//          </nameset>
+//          <fileset>
+//              <file>Roboto-Thin.ttf</file>
+//              <file>Roboto-ThinItalic.ttf</file>
+//          </fileset>
+//      </family>
+//  </familyset>
+
+struct Font {
+    filename: String,
+    weight: Option<i32>,
+}
+
+struct FontFamily {
+    name: String,
+    fonts: Vec<Font>,
+}
+
+struct FontAlias {
+    from: String,
+    to: String,
+    weight: Option<i32>
+}
+
+struct FontList {
+    families: Vec<FontFamily>,
+    aliases: Vec<FontAlias>
+}
+
+impl FontList {
+    fn new() -> FontList {
+        // Possible paths containing the font mapping xml file.
+        let paths = [
+            "/etc/fonts.xml",
+            "/system/etc/system_fonts.xml"
+        ];
+
+        // Try to load and parse paths until one of them success.
+        let mut result = None;
+        paths.iter().all(|path| {
+            result = Self::from_path(path);
+            !result.is_some()
+        });
+
+        match result {
+            Some(result) => result,
+            // If no xml mapping file is found fallback to some default
+            // fonts expected to be on all Android devices.
+            None => FontList {
+                families: Self::fallback_font_families(),
+                aliases: Vec::new(),
+            }
+        }
+    }
+
+    // Creates a new FontList from a path to the font mapping xml file.
+    fn from_path(path: &str) -> Option<FontList> {
+        let xml = match Self::load_file(path) {
+            Ok(xml) => xml,
+            _=> { return None; },
+        };
+
+        let dom: RcDom = parse_document(RcDom::default(), Default::default())
+                         .one(xml);
+        let doc = &dom.document;
+
+        // find familyset root node
+        let children = doc.children.borrow();
+        let familyset = children.iter().find(|child| {
+            match child.data {
+                NodeData::Element { ref name, .. } => &*name.local == "familyset",
+                _ => false,
+            }
+        });
+
+        let familyset = match familyset {
+            Some(node) => node,
+            _ => { return None; }
+        };
+
+        // Parse familyset node
+        let mut families = Vec::new();
+        let mut aliases = Vec::new();
+
+        for node in familyset.children.borrow().iter() {
+            match node.data {
+                NodeData::Element { ref name, ref attrs, .. } => {
+                    if &*name.local == "family" {
+                        Self::parse_family(&node, attrs, &mut families);
+                    } else if &*name.local == "alias" {
+                        // aliases come after the fonts they reference. -->
+                        if !families.is_empty() {
+                            Self::parse_alias(attrs, &mut aliases);
+                        }
+                    }
+                },
+                _=> {}
+            }
+        }
+
+        Some(FontList {
+            families: families,
+            aliases: aliases
+        })
+    }
+
+    // Fonts expected to exist in Android devices.
+    // Only used in the unlikely case where no font xml mapping files are found.
+    fn fallback_font_families() -> Vec<FontFamily> {
+        let alternatives = [
+            ("san-serif", "Roboto-Regular.ttf"),
+            ("Droid Sans", "DroidSans.ttf"),
+        ];
+
+        alternatives.iter().filter(|item| {
+            Path::new(&Self::font_absolute_path(item.1)).exists()
+        }).map(|item| {
+            FontFamily {
+                name: item.0.into(),
+                fonts: vec![Font {
+                    filename: item.1.into(),
+                    weight: None,
+                }]
+            }
+        }). collect()
+    }
+
+    // All Android fonts are located in /system/fonts
+    fn font_absolute_path(filename: &str) -> String {
+        format!("/system/fonts/{}", filename)
+    }
+
+    fn find_family(&self, name: &str) -> Option<&FontFamily>{
+        self.families.iter().find(|f| f.name == name)
+    }
+
+    fn find_alias(&self, name: &str) -> Option<&FontAlias>{
+        self.aliases.iter().find(|f| f.from == name)
+    }
+
+
+    fn load_file(path: &str) -> Result<String, io::Error> {
+        let mut file = try!(File::open(path));
+        let mut content = String::new();
+        try!(file.read_to_string(&mut content));
+
+        Ok(content)
+    }
+
+    // Parse family and font file names
+    // Example:
+    // <family name="sans-serif">
+    //     <font weight="100" style="normal">Roboto-Thin.ttf</font>
+    //     <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+    //     <font weight="300" style="normal">Roboto-Light.ttf</font>
+    //     <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
+    //     <font weight="400" style="normal">Roboto-Regular.ttf</font>
+    // </family>
+    fn parse_family(familyset: &Node, attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<FontFamily>) {
+        // Fallback to old Android API v17 xml format if required
+        let using_api_17 = familyset.children.borrow().iter().any(|node| {
+            match node.data {
+                NodeData::Element { ref name, .. } => &*name.local == "nameset",
+                _=> false,
+            }
+        });
+        if using_api_17 {
+            Self::parse_family_v17(familyset, out);
+            return;
+        }
+
+        // Parse family name
+        let name = match Self::find_attrib("name", attrs) {
+            Some(name) => name,
+            _ => { return; },
+        };
+
+        let mut fonts = Vec::new();
+        // Parse font variants
+        for node in familyset.children.borrow().iter() {
+            match node.data {
+                NodeData::Element { ref name, ref attrs, .. } => {
+                    if &*name.local == "font" {
+                        FontList::parse_font(&node, attrs, &mut fonts);
+                    }
+                },
+                _=> {}
+            }
+        }
+
+        out.push(FontFamily {
+            name: name,
+            fonts: fonts
+        });
+    }
+
+    // Parse family and font file names for Androi API < 21
+    // Example:
+    // <family>
+    //     <nameset>
+    //         <name>sans-serif</name>
+    //         <name>arial</name>
+    //         <name>helvetica</name>
+    //         <name>tahoma</name>
+    //         <name>verdana</name>
+    //     </nameset>
+    //     <fileset>
+    //         <file>Roboto-Regular.ttf</file>
+    //         <file>Roboto-Bold.ttf</file>
+    //         <file>Roboto-Italic.ttf</file>
+    //         <file>Roboto-BoldItalic.ttf</file>
+    //     </fileset>
+    // </family>
+    fn parse_family_v17(familyset: &Node, out:&mut Vec<FontFamily>) {
+        let mut nameset = Vec::new();
+        let mut fileset = Vec::new();
+        for node in familyset.children.borrow().iter() {
+            match node.data {
+                NodeData::Element { ref name, .. } => {
+                    if &*name.local == "nameset" {
+                        Self::collect_contents_with_tag(node, "name", &mut nameset);
+                    } else if &*name.local == "fileset" {
+                        Self::collect_contents_with_tag(node, "file", &mut fileset);
+                    }
+                },
+                _=> {}
+            }
+        }
+
+        // Create a families for each variation
+        for name in nameset {
+            let fonts: Vec<Font> = fileset.iter().map(|f| Font {
+                filename: f.clone(),
+                weight: None,
+            }).collect();
+
+            if !fonts.is_empty() {
+                out.push(FontFamily {
+                    name: name,
+                    fonts: fonts
+                })
+            }
+        }
+    }
+
+    // Example:
+    // <font weight="100" style="normal">Roboto-Thin.ttf</font>
+    fn parse_font(node: &Node, attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<Font>) {
+        // Parse font filename
+        let filename = match Self::text_content(node) {
+            Some(filename) => filename,
+            _ => { return; }
+        };
+
+        // Parse font weight
+        let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
+
+        out.push(Font {
+            filename: filename,
+            weight: weight,
+        })
+    }
+
+    // Example:
+    // <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+    // <alias name="sans-serif-light" to="sans-serif" weight="300" />
+    // <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+    // <alias name="sans-serif-black" to="sans-serif" weight="900" />
+    // <alias name="arial" to="sans-serif" />
+    // <alias name="helvetica" to="sans-serif" />
+    // <alias name="tahoma" to="sans-serif" />
+    // <alias name="verdana" to="sans-serif" />
+    fn parse_alias(attrs: &RefCell<Vec<Attribute>>, out:&mut Vec<FontAlias>) {
+        // Parse alias name and referenced font
+        let from = match Self::find_attrib("name", attrs) {
+            Some(from) => from,
+            _ => { return; },
+        };
+
+        // Parse referenced font
+        let to = match Self::find_attrib("to", attrs) {
+            Some(to) => to,
+            _ => { return; },
+        };
+
+        // Parse optional weight filter
+        let weight = Self::find_attrib("weight", attrs).and_then(|w| w.parse().ok());
+
+        out.push(FontAlias {
+            from: from,
+            to: to,
+            weight: weight,
+        })
+    }
+
+    fn find_attrib(name: &str, attrs: &RefCell<Vec<Attribute>>) -> Option<String> {
+        attrs.borrow().iter().find(|attr| &*attr.name.local == name).map(|s| String::from(&s.value))
+    }
+
+    fn text_content(node: &Node) -> Option<String> {
+        node.children.borrow().get(0).and_then(|child| {
+            match child.data {
+                NodeData::Text { ref contents } => {
+                    let mut result = String::new();
+                    result.push_str(&contents.borrow());
+                    Some(result)
+                },
+                _ => None
+            }
+        })
+    }
+
+    fn collect_contents_with_tag(node: &Node, tag: &str, out:&mut Vec<String>) {
+        for child in node.children.borrow().iter() {
+            match child.data {
+                NodeData::Element { ref name, .. } => {
+                    if &*name.local == tag {
+                        if let Some(content) = Self::text_content(child) {
+                            out.push(content);
+                        }
+                    }
+                },
+                _=> {}
+            }
+        }
+    }
+}
+
+// Functions used by FontCacheThread
+pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) {
+    for family in &FONT_LIST.families {
+        callback(family.name.clone());
+    }
+    for alias in &FONT_LIST.aliases {
+        callback(alias.from.clone());
+    }
+}
+
+pub fn for_each_variation<F>(family_name: &str, mut callback: F)
+    where F: FnMut(String)
+{
+    println!("Variatioooon {:?}", family_name);
+    if let Some(family) = FONT_LIST.find_family(family_name) {
+        for font in &family.fonts {
+            callback(FontList::font_absolute_path(&font.filename));
+        }
+        return;
+    }
+
+    if let Some(alias) = FONT_LIST.find_alias(family_name) {
+        if let Some(family) = FONT_LIST.find_family(&alias.to) {
+            for font in &family.fonts {
+                match (alias.weight, font.weight) {
+                    (None, _) => callback(FontList::font_absolute_path(&font.filename)),
+                    (Some(w1), Some(w2)) => {
+                        if w1 == w2 {
+                            callback(FontList::font_absolute_path(&font.filename))
+                        }
+                    },
+                    _ => {}
+                }
+            }
+        }
+    }
+}
+
+pub fn system_default_family(generic_name: &str) -> Option<String> {
+    if let Some(family) = FONT_LIST.find_family(&generic_name) {
+        Some(family.name.clone())
+    } else if let Some(alias) = FONT_LIST.find_alias(&generic_name) {
+        Some(alias.from.clone())
+    } else {
+        //  First font defined in the fonts.xml is the default on Android.
+        FONT_LIST.families.get(0).map(|family| family.name.clone())
+    }
+}
+
+pub fn last_resort_font_families() -> Vec<String> {
+    vec!(
+        "sans-serif".to_owned(),
+        "Droid Sans".to_owned(),
+        "serif".to_owned(),
+    )
+}
+
+pub static SANS_SERIF_FONT_FAMILY: &'static str = "sans-serif";
--- a/servo/components/gfx/platform/freetype/font_list.rs
+++ b/servo/components/gfx/platform/freetype/font_list.rs
@@ -136,29 +136,21 @@ pub fn system_default_family(generic_nam
 pub fn last_resort_font_families() -> Vec<String> {
     vec!(
         "Fira Sans".to_owned(),
         "DejaVu Sans".to_owned(),
         "Arial".to_owned()
     )
 }
 
-#[cfg(target_os = "android")]
-pub fn last_resort_font_families() -> Vec<String> {
-    vec!("Roboto".to_owned())
-}
-
 #[cfg(target_os = "windows")]
 pub fn last_resort_font_families() -> Vec<String> {
     vec!(
         "Arial".to_owned()
     )
 }
 
-#[cfg(target_os = "android")]
-pub static SANS_SERIF_FONT_FAMILY: &'static str = "Roboto";
-
 #[cfg(target_os = "linux")]
 pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans";
 
 #[cfg(target_os = "windows")]
 pub static SANS_SERIF_FONT_FAMILY: &'static str = "Arial";
 
--- a/servo/components/gfx/platform/mod.rs
+++ b/servo/components/gfx/platform/mod.rs
@@ -24,18 +24,24 @@ mod freetype {
     /// Panics if the buffer does not contain UTF-8.
     unsafe fn c_str_to_string(s: *const c_char) -> String {
         str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap().to_owned()
     }
 
     pub mod font;
     pub mod font_context;
 
-    #[cfg(any(target_os = "linux", target_os = "android"))]
+    #[cfg(target_os = "linux")]
     pub mod font_list;
+    #[cfg(target_os = "android")]
+    mod android {
+        pub mod font_list;
+    }
+    #[cfg(target_os = "android")]
+    pub use self::android::font_list;
 
     #[cfg(any(target_os = "linux", target_os = "android"))]
     pub mod font_template;
 }
 
 #[cfg(target_os = "macos")]
 mod macos {
     pub mod font;
--- a/servo/ports/servo/main.rs
+++ b/servo/ports/servo/main.rs
@@ -199,17 +199,17 @@ impl app::NestedEventLoopListener for Br
         }
         true
     }
 }
 
 #[cfg(target_os = "android")]
 fn setup_logging() {
     // Piping logs from stdout/stderr to logcat happens in android_injected_glue.
-    ::std::env::set_var("RUST_LOG", "debug");
+    ::std::env::set_var("RUST_LOG", "error");
 
     unsafe { android_injected_glue::ffi::app_dummy() };
 }
 
 #[cfg(not(target_os = "android"))]
 fn setup_logging() {}
 
 #[cfg(target_os = "android")]