| // |
| // Copyright (C) 2025 The Android Open-Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! The crate providing the functionality to manage the native application process. |
| |
| use activitymanager_structured_aidl::aidl::android::app::IActivityManagerStructured::IActivityManagerStructured; |
| use anyhow::{Context, Result}; |
| use binder::{BinderFeatures, ProcessState, Strong}; |
| use log::{error, info, warn, LevelFilter}; |
| use native_activity_thread_bindgen::android_set_application_target_sdk_version; |
| use native_application_thread_aidl::aidl::android::app::INativeApplicationThread::BnNativeApplicationThread; |
| use nix::sys::signal::{pthread_sigmask, SigSet, SigmaskHow, Signal}; |
| use rustutils::android::process::{android_mallopt, MalloptOpcode}; |
| |
| use utils::{apply_runtime_flags, setup_process_dumpability}; |
| |
| mod font; |
| mod library_loader; |
| mod native_activity_thread; |
| mod native_application_thread; |
| mod preload; |
| mod task; |
| mod utils; |
| |
| pub use preload::preload_lib; |
| pub use utils::get_or_init_debuggable; |
| |
| use crate::native_activity_thread::NativeActivityThread; |
| use crate::native_application_thread::NativeApplicationThread; |
| use crate::task::{run_thread_loop, Handler}; |
| |
| static ACTIVITY_MANAGER_SERVICE_NAME: &str = "activity_structured"; |
| |
| // Must be the same value as `SdkVersion::kUnset` in art/libartbase/base/sdk_version.h. |
| const SDK_VERSION_UNSET: i32 = 0; |
| |
| /// Initialize a process for usage with an Android native application. Used when |
| /// Zygote forks and transition directly into an app process, or when starting |
| /// an App Zygote. |
| pub fn app_process_init(target_sdk_version: i32, runtime_flags: u32) { |
| setup_process_dumpability().expect("Failed to set up process dumpability"); |
| |
| // SAFETY: This opcode takes no arguments so a nullptr is passed |
| // instead. |
| let ret = unsafe { android_mallopt(MalloptOpcode::SetZygoteChild, std::ptr::null_mut(), 0) }; |
| if ret.is_err() { |
| log::error!("Call to android_mallopt failed: Opcode = M_SET_ZYGOTE_CHILD"); |
| } |
| |
| if let Err(e) = apply_runtime_flags(runtime_flags) { |
| panic!("Failed to apply runtime flags: {e}"); |
| } |
| |
| let target = if target_sdk_version <= 0 { SDK_VERSION_UNSET } else { target_sdk_version }; |
| // SAFETY: target is a valid SDK version validated above. |
| unsafe { |
| android_set_application_target_sdk_version(target); |
| } |
| } |
| |
| /// Start NativeActivityThread to manage the process. |
| pub fn run_native_activity_thread(start_seq: i64) -> ! { |
| logger::init( |
| logger::Config::default() |
| .with_tag_on_device("native_activity_thread") |
| .with_max_level(LevelFilter::Trace), |
| ); |
| info!("Hello from the native activity thread! start_seq={start_seq}"); |
| |
| run_signal_catcher_thread().unwrap(); |
| |
| // This must be done before creating any Binder client or server. |
| ProcessState::start_thread_pool(); |
| |
| let activity_manager = get_activity_manager_proxy().unwrap(); |
| |
| // Prepare the handler of INativeApplicationThread requests from the ActivityManager |
| let handler = Handler::new_on_current_thread(NativeActivityThread::new( |
| activity_manager.clone(), |
| start_seq, |
| )) |
| .unwrap(); |
| |
| let sender = handler.get_sender().unwrap(); |
| let binder_node = BnNativeApplicationThread::new_binder( |
| NativeApplicationThread::new(sender), |
| BinderFeatures::default(), |
| ); |
| |
| // Notify the ActivityManager that this process is ready to be used for application. |
| activity_manager.attachNativeApplication(&binder_node.as_binder(), start_seq).unwrap(); |
| |
| // Start the main thread loop. |
| run_thread_loop().unwrap(); |
| |
| panic!("Shouldn't come here!"); |
| } |
| |
| fn get_activity_manager_proxy() -> Result<Strong<dyn IActivityManagerStructured>> { |
| binder::check_interface(ACTIVITY_MANAGER_SERVICE_NAME).context("Failed to find ActivityManager") |
| } |
| |
| fn run_signal_catcher_thread() -> Result<()> { |
| let mut sigset = SigSet::empty(); |
| |
| // SIGUSR1 is used to notify ANR, and SIGQUIT is used to actually terminate the process due to |
| // the ANR. SIGPIPE is also blocked here to follow the design of Zygote. |
| for signal in [Signal::SIGUSR1, Signal::SIGQUIT, Signal::SIGPIPE] { |
| sigset.add(signal); |
| } |
| |
| pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(&sigset), None) |
| .context("pthread_sigmask failed")?; |
| |
| std::thread::Builder::new() |
| .name("signal_catcher".to_string()) |
| .spawn(move || loop { |
| // At 50% of the ANR timeout, AMS triggers long method tracing using SIGUSR1. |
| // Since we don't support this, we catch the signal and ignore it. |
| // (SIGUSR1 is also used to invoke GC explicitly, which is not supported as well.) |
| // |
| // At 100% of the ANR timeout, AMS executes the following sequence in appNotResponding(): |
| // 1. debuggerd_trigger_dump(kDebuggerdJavaBacktrace): debuggerd sends SIGQUIT. We must ignore this to allow the native stack trace to proceed. |
| // 2. debuggerd_trigger_dump(kDebuggerdNativeBacktrace): debuggerd sends BIONIC_SIGNAL_DEBUGGER which is handled transparently by the signal handler set up by bionic. |
| // 3. AMS kills the app. |
| match sigset.wait() { |
| Ok(Signal::SIGUSR1) => { |
| info!("Received SIGUSR1"); |
| } |
| Ok(Signal::SIGQUIT) => { |
| info!("Received SIGQUIT"); |
| } |
| Ok(signum) => { |
| warn!("Signal catcher thread received unexpected signal: {signum:?}"); |
| } |
| Err(e) => { |
| error!("Error waiting for signal: {e}"); |
| break; |
| } |
| } |
| }) |
| .context("Failed to spawn the signal_catcher thread")?; |
| Ok(()) |
| } |