Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  mod.rs   Sprache: unbekannt

 
// 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 https://mozilla.org/MPL/2.0/.

use std::env;
use std::ffi::{c_char, CStr, CString};
use std::ops::DerefMut;
use std::path::PathBuf;
use std::time::Duration;

use firefox_on_glean::{metrics, pings};
use nserror::{nsresult, NS_ERROR_FAILURE};
use nsstring::{nsACString, nsCString, nsString};
use xpcom::interfaces::{
    mozILocaleService, nsIFile, nsIPrefService, nsIProperties, nsIXULAppInfo, nsIXULRuntime,
};
use xpcom::{RefPtr, XpCom};

use glean::{ClientInfoMetrics, Configuration};

#[cfg(not(target_os = "android"))]
mod upload_pref;
#[cfg(not(target_os = "android"))]
mod user_activity;
mod viaduct_uploader;

#[cfg(not(target_os = "android"))]
use upload_pref::UploadPrefObserver;
#[cfg(not(target_os = "android"))]
use user_activity::UserActivityObserver;
use viaduct_uploader::ViaductUploader;

/// Project FOG's entry point.
///
/// This assembles client information and the Glean configuration and then initializes the global
/// Glean instance.
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub extern "C" fn fog_init(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
    disable_internal_pings: bool,
) -> nsresult {
    let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled");
    let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
    let uploader = Some(Box::new(ViaductUploader) as Box<dyn glean::net::PingUploader>);

    fog_init_internal(
        data_path_override,
        app_id_override,
        upload_enabled || recording_enabled,
        uploader,
        // Flipping it around, because no value = defaults to false,
        // so we take in `disable` but pass on `enable`.
        !disable_internal_pings,
    )
    .into()
}

/// Project FOG's entry point on Android.
///
/// This assembles client information and the Glean configuration and then initializes the global
/// Glean instance.
/// It always enables upload and set no uploader.
/// This should only be called in test scenarios.
/// In normal use Glean should be initialized and controlled by the Glean Kotlin SDK.
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "C" fn fog_init(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
    disable_internal_pings: bool,
) -> nsresult {
    // On Android always enable Glean upload.
    let upload_enabled = true;
    // Don't set up an uploader.
    let uploader = None;

    fog_init_internal(
        data_path_override,
        app_id_override,
        upload_enabled,
        uploader,
        !disable_internal_pings,
    )
    .into()
}

fn fog_init_internal(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
    upload_enabled: bool,
    uploader: Option<Box<dyn glean::net::PingUploader>>,
    enable_internal_pings: bool,
) -> Result<(), nsresult> {
    metrics::fog::initialization.start();

    log::debug!("Initializing FOG.");

    setup_observers()?;

    let (mut conf, client_info) = build_configuration(data_path_override, app_id_override)?;

    conf.upload_enabled = upload_enabled;
    conf.uploader = uploader;
    conf.enable_internal_pings = enable_internal_pings;

    // If we're operating in automation without any specific source tags to set,
    // set the tag "automation" so any pings that escape don't clutter the tables.
    // See https://mozilla.github.io/glean/book/user/debugging/index.html#enabling-debugging-features-through-environment-variables
    if env::var("MOZ_AUTOMATION").is_ok() && env::var("GLEAN_SOURCE_TAGS").is_err() {
        log::info!("In automation, setting 'automation' source tag.");
        glean::set_source_tags(vec!["automation".to_string()]);
        log::info!("In automation, disabling MPS to avoid 4am issues.");
        conf.use_core_mps = false;
    }

    log::debug!("Configuration: {:#?}", conf);

    // Register all custom pings before we initialize.
    pings::register_pings(Some(&conf.application_id));

    glean::initialize(conf, client_info);

    metrics::fog::initialization.stop();

    Ok(())
}

fn build_configuration(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
) -> Result<(Configuration, ClientInfoMetrics), nsresult> {
    let data_path_str = if data_path_override.is_empty() {
        get_data_path()?
    } else {
        data_path_override.to_utf8().to_string()
    };
    let data_path = PathBuf::from(&data_path_str);

    let (app_build, app_display_version, channel, locale) = get_app_info()?;

    let client_info = ClientInfoMetrics {
        app_build,
        app_display_version,
        channel: Some(channel),
        locale: Some(locale),
    };
    log::debug!("Client Info: {:#?}", client_info);

    let localhost_port = static_prefs::pref!("telemetry.fog.test.localhost_port");
    let server = if localhost_port > 0 {
        format!("http://localhost:{}", localhost_port)
    } else {
        if app_id_override == "thunderbird.desktop" {
            String::from("https://incoming.thunderbird.net")
        } else {
            String::from("https://incoming.telemetry.mozilla.org")
        }
    };

    let application_id = if app_id_override.is_empty() {
        "firefox.desktop".to_string()
    } else {
        app_id_override.to_utf8().to_string()
    };

    extern "C" {
        fn FOG_MaxPingLimit() -> u32;
    }

    // SAFETY NOTE: Safe because it returns a primitive by value.
    let pings_per_interval = unsafe { FOG_MaxPingLimit() };
    metrics::fog::max_pings_per_minute.set(pings_per_interval.into());

    let rate_limit = Some(glean::PingRateLimit {
        seconds_per_interval: 60,
        pings_per_interval,
    });

    let configuration = Configuration {
        upload_enabled: false,
        data_path,
        application_id,
        max_events: None,
        delay_ping_lifetime_io: true,
        server_endpoint: Some(server),
        uploader: None,
        use_core_mps: true,
        trim_data_to_registered_pings: true,
        log_level: None,
        rate_limit,
        enable_event_timestamps: true,
        experimentation_id: None,
        enable_internal_pings: true,
        ping_schedule: pings::ping_schedule(),
        ping_lifetime_threshold: 0,
        ping_lifetime_max_time: Duration::ZERO,
    };

    Ok((configuration, client_info))
}

#[cfg(not(target_os = "android"))]
fn setup_observers() -> Result<(), nsresult> {
    if let Err(e) = UploadPrefObserver::begin_observing() {
        log::error!(
            "Could not observe data upload pref. Abandoning FOG init due to {:?}",
            e
        );
        return Err(e);
    }

    if let Err(e) = UserActivityObserver::begin_observing() {
        log::error!(
            "Could not observe user activity. Abandoning FOG init due to {:?}",
            e
        );
        return Err(e);
    }

    Ok(())
}

#[cfg(target_os = "android")]
fn setup_observers() -> Result<(), nsresult> {
    // No observers are set up on Android.
    Ok(())
}

/// Construct and return the data_path from the profile dir, or return an error.
fn get_data_path() -> Result<String, nsresult> {
    let dir_svc: RefPtr<nsIProperties> = match xpcom::components::Directory::service() {
        Ok(ds) => ds,
        _ => return Err(NS_ERROR_FAILURE),
    };
    let mut profile_dir = xpcom::GetterAddrefs::<nsIFile>::new();
    unsafe {
        dir_svc
            .Get(
                cstr!("ProfD").as_ptr(),
                &nsIFile::IID,
                profile_dir.void_ptr(),
            )
            .to_result()?;
    }
    let profile_dir = profile_dir.refptr().ok_or(NS_ERROR_FAILURE)?;
    let mut profile_path = nsString::new();
    unsafe {
        (*profile_dir).GetPath(&mut *profile_path).to_result()?;
    }
    let profile_path = String::from_utf16(&profile_path[..]).map_err(|_| NS_ERROR_FAILURE)?;
    let data_path = profile_path + "/datareporting/glean";
    Ok(data_path)
}

/// Return a tuple of the build_id, app version, build channel, and locale.
/// If the XUL Runtime isn't a XULAppInfo (e.g. in xpcshell),
/// build_id will be "unknown".
/// Other problems result in an error being returned instead.
fn get_app_info() -> Result<(String, String, String, String), nsresult> {
    let xul: RefPtr<nsIXULRuntime> =
        xpcom::components::XULRuntime::service().map_err(|_| NS_ERROR_FAILURE)?;

    let pref_service: RefPtr<nsIPrefService> =
        xpcom::components::Preferences::service().map_err(|_| NS_ERROR_FAILURE)?;
    let locale_service: RefPtr<mozILocaleService> =
        xpcom::components::Locale::service().map_err(|_| NS_ERROR_FAILURE)?;
    let branch = xpcom::getter_addrefs(|p| {
        // Safe because:
        //  * `null` is explicitly allowed per documentation
        //  * `p` is a valid outparam guaranteed by `getter_addrefs`
        unsafe { pref_service.GetDefaultBranch(std::ptr::null(), p) }
    })?;
    let pref_name = CString::new("app.update.channel").map_err(|_| NS_ERROR_FAILURE)?;
    let mut channel = nsCString::new();
    // Safe because:
    //  * `branch` is non-null (otherwise `getter_addrefs` would've been `Err`
    //  * `pref_name` exists so a pointer to it is valid for the life of the function
    //  * `channel` exists so a pointer to it is valid, and it can be written to
    unsafe {
        if (*branch)
            .GetCharPref(pref_name.as_ptr(), channel.deref_mut() as *mut nsACString)
            .to_result()
            .is_err()
        {
            channel = "unknown".into();
        }
    }

    extern "C" {
        fn FOG_MozAppVersionDisplay() -> *const c_char;
    }
    // SAFETY: It's literally a quoted literal.
    let version = unsafe { CStr::from_ptr(FOG_MozAppVersionDisplay()) }
        .to_str()
        .map_err(|_| NS_ERROR_FAILURE)?;

    let app_info = match xul.query_interface::<nsIXULAppInfo>() {
        Some(ai) => ai,
        // In e.g. xpcshell the XULRuntime isn't XULAppInfo.
        // We still want to return sensible values so tests don't explode.
        _ => {
            return Ok((
                "unknown".to_owned(),
                version.to_string(),
                channel.to_string(),
                "unknown".to_owned(),
            ))
        }
    };

    let mut build_id = nsCString::new();
    unsafe {
        app_info.GetAppBuildID(&mut *build_id).to_result()?;
    }

    let mut locale = nsCString::new();
    unsafe {
        locale_service
            .GetAppLocaleAsBCP47(&mut *locale)
            .to_result()?;
    }

    Ok((
        build_id.to_string(),
        version.to_string(),
        channel.to_string(),
        locale.to_string(),
    ))
}

/// **TEST-ONLY METHOD**
/// Resets FOG and the underlying Glean SDK, clearing stores.
#[no_mangle]
pub extern "C" fn fog_test_reset(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
) -> nsresult {
    fog_test_reset_internal(data_path_override, app_id_override).into()
}

// Split out into its own function so I could use `?`
#[cfg(not(target_os = "android"))]
fn fog_test_reset_internal(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
) -> Result<(), nsresult> {
    let (mut conf, client_info) = build_configuration(data_path_override, app_id_override)?;

    let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled");
    let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
    conf.upload_enabled = upload_enabled || recording_enabled;

    // Don't accidentally send "main" pings during tests.
    conf.use_core_mps = false;

    // I'd prefer to reuse the uploader, but it gets moved into Glean so we build anew.
    conf.uploader = Some(Box::new(ViaductUploader) as Box<dyn glean::net::PingUploader>);

    // Register all custom pings before we initialize.
    pings::register_pings(None);

    glean::test_reset_glean(conf, client_info, true);
    Ok(())
}

#[cfg(target_os = "android")]
fn fog_test_reset_internal(
    data_path_override: &nsACString,
    app_id_override: &nsACString,
) -> Result<(), nsresult> {
    let (mut conf, client_info) = build_configuration(data_path_override, app_id_override)?;

    // On Android always enable Glean upload.
    conf.upload_enabled = true;

    // Don't accidentally send "main" pings during tests.
    conf.use_core_mps = false;

    // Same as before, would prefer to reuse, but it gets moved into Glean so we build anew.
    conf.uploader = Some(Box::new(ViaductUploader) as Box<dyn glean::net::PingUploader>);

    glean::test_reset_glean(conf, client_info, true);
    Ok(())
}

[ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge