Running Services Monitor

repository·main·Indexed 43 days ago

https://github.com/biplobsd/running_services_monitor

An Android utility built with Flutter that allows users to view and monitor all currently running system and user-installed services. It bypasses Android 8.0+ API restrictions by utilizing root access or the Shizuku app to execute privileged ADB commands and parse dumpsys activity services output.

Tokens
6.5K
Snippets
22
Records
29
Agent score
58%

What's inside Running Services Monitor

  1. How Running Services Monitor works

    main

    The app monitors running services by executing privileged ADB commands. Because the ActivityManager.getRunningServices() API was restricted in Android 8.0 (API 26) for privacy, the app uses one of two methods to bypass these restrictions:

    1. Permission Check: The app first attempts to detect Root access.
    2. Root Path: If root is available, it executes commands directly with superuser privileges.
    3. Shizuku Path: If root is unavailable, it connects to the Shizuku service (which uses ADB/Wireless Debugging) to execute commands.
    4. Data Extraction: The app executes dumpsys activity services to retrieve service information.
    5. Parsing & Display: It parses the dumpsys output to extract details like package name, PID, and process name, then categorizes them into System or User apps.
  2. Install Running Services Monitor on a Rooted Device

    main

    If your Android device is rooted, follow these steps for the recommended installation method:

    1. Install the app from your preferred source (Google Play, F-Droid, etc.).
    2. When prompted by the system, grant the app root permission.
    3. The app will automatically use root access for all monitoring operations.
  3. Install Running Services Monitor on a Non-Rooted Device using Shizuku

    main

    If your device is not rooted, you must use the Shizuku app to provide the necessary permissions for monitoring services.

    1. Install Shizuku

    Download and install Shizuku from the Google Play Store or GitHub Releases.

    2. Start Shizuku Service (via Wireless Debugging)

    1. Enable Developer Options on your Android device.
    2. Enable Wireless Debugging within the Developer Options menu.
    3. Open the Shizuku app.
    4. Tap "Pair" or "Start" and follow the on-screen instructions to complete the pairing process.
  4. Add new translations to the project

    main

    To add support for a new language, you must update both the Flutter localization files and the Android resource configuration.

    1. Flutter Localization

    • Create a new app_<localeName>.arb file in lib/l10n/ (e.g., app_es.arb for Spanish).
    • Translate all fields from the base app_en.arb file, ensuring keys remain unchanged.
    • Register the new language in lib/core/constants.dart within the languages list:
    static const List<Lang> languages = [
      Lang(Locale('en'), 'English'),
      Lang(Locale('bn'), 'বাংলা'),
      Lang(Locale('zh'), '简体中文'),
      Lang(Locale('<localeName>'), '<Native Language Name>'), // Add your new entry here
    ];

    2. Android Configuration

    Add the new locale to android/app/src/main/res/xml/locales_config.xml:

    <locale android:name="<localeName>" />

    3. Generate Files

    Run these commands in the project root to apply changes:

    flutter gen-l10n
    dart run build_runner build --delete-conflicting-outputs
    static const List<Lang> languages = [
      Lang(Locale('en'), 'English'),
      Lang(Locale('bn'), 'বাংলা'),
      Lang(Locale('zh'), '简体中文'),
      Lang(Locale('<localeName>'), '<Native Language Name>'), // Add here
    ];
  5. Build Running Services Monitor from source

    main

    To build the project locally, ensure you have Flutter and Dart installed. Follow these steps:

    1. Clone the repository and navigate to the directory.
    2. Fetch dependencies.
    3. Generate necessary code (Freezed models and localization).
    4. Run or build the application.

    Available build targets:

    • Debug: Run directly on a connected device.
    • Release APK: A single universal APK.
    • Split APK: Smaller APKs split per ABI.
    • App Bundle: For Google Play Store distribution.
    # Clone the repository
    git clone https://github.com/biplobsd/running_services_monitor.git
    cd running_services_monitor
    
    # Get dependencies
    flutter pub get
    
    # Generate code (Freezed models and localization)
    flutter gen-l10n
    dart run build_runner build --delete-conflicting-outputs
    
    # Run on connected device
    flutter run
    
    # Build release APK (universal)
    flutter build apk --release
    
    # Build release APK (split per ABI for smaller file sizes)
    flutter build apk --release --split-per-abi
    
    # Build App Bundle for Play Store
    flutter build appbundle --release
  6. Configure application lifecycle and data loading

    main

    The application uses WidgetsBindingObserver to respond to lifecycle changes. When the app moves to the resumed state, it triggers a data reload via the HomeBloc to ensure the service monitoring information is up to date.

    Key lifecycle event:

    • AppLifecycleState.resumed: Dispatches HomeEvent.loadData(silent: true, notify: true) to the HomeBloc.
    @override
    void didChangeAppLifecycleState(AppLifecycleState state) {
      if (state == AppLifecycleState.resumed) {
        final homeBloc = getIt<HomeBloc>();
        homeBloc.add(const HomeEvent.loadData(silent: true, notify: true));
      }
    }
  7. Initialize the Running Services Monitor application

    main

    The application entrypoint initializes several core systems:

    • AppIconCache: Caches application icons.
    • HydratedBloc: Sets up persistent storage for BLoC states using the application's documents directory.
    • Dependency Injection: Calls configureDependencies() to register all required services and BLoCs.
    • System UI: Configures an edge-to-edge display with transparent status and navigation bars.

    Note: The initialization is wrapped in a try-catch block to prevent app crashes during early startup failures.

    void main() async {
      try {
        WidgetsFlutterBinding.ensureInitialized();
        await AppIconCache.initialize();
        SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
        SystemChrome.setSystemUIOverlayStyle(
          const SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
            systemNavigationBarColor: Colors.transparent,
            systemNavigationBarDividerColor: Colors.transparent,
          ),
        );
        HydratedBloc.storage = await HydratedStorage.build(
          storageDirectory: HydratedStorageDirectory((await getApplicationDocumentsDirectory()).path),
        );
    
        configureDependencies();
        runApp(const MyApp());
      } catch (_) {}
    }
  8. Use CustomScrollProvider to manage tabbed scrolling

    main

    The CustomScrollProvider is a widget used to manage a collection of CustomScrollController instances synchronized with a TabController. It is designed for scenarios where multiple scrollable views (one per tab) share a single parent ScrollController but need to be attached or detached dynamically based on the active tab to optimize performance or behavior.

    To use it, wrap your tabbed content with CustomScrollProvider and provide the following:

    • tabController: The TabController managing the tab state.
    • parent: The primary ScrollController that the individual tab controllers will delegate to.
    • child: The widget tree containing the tabs.

    Inside the child tree, you can access the list of controllers using CustomScrollProviderData.of(context).

    CustomScrollProvider(
      tabController: myTabController,
      parent: myParentScrollController,
      child: MyTabbedContentWidget(),
    )
  9. Initialize dependency injection with init()

    main

    The project uses get_it and injectable for dependency injection. To initialize the registration of main-scope dependencies, call the init() extension method on a GetIt instance. This method registers various BLoCs, services (like ShizukuService and ProcessService), and APIs into the service locator.

    Available parameters:

    • environment (String?): An optional environment name to filter dependencies.
    • environmentFilter (EnvironmentFilter?): An optional filter to control which dependencies are registered based on the environment.
    import 'package:get_it/get_it.dart' as get_it;
    import 'package:your_project/core/dependency_injection/dependency_injection.config.dart';
    
    final getIt = get_it.GetIt.instance;
    getIt.init();
  10. Access scroll controllers via CustomScrollProviderData

    main

    To retrieve the list of CustomScrollController instances managed by a CustomScrollProvider within its subtree, use the CustomScrollProviderData.of method. This returns the CustomScrollProviderData inherited widget, which contains the scrollControllers list.

    final providerData = CustomScrollProviderData.of(context);
    final List<CustomScrollController> controllers = providerData.scrollControllers;
  11. Use ShizukuHostApi to communicate with Shizuku

    main

    The ShizukuHostApi class provides a set of asynchronous methods to interact with the Shizuku service on Android. It allows for executing shell commands, checking/requesting permissions (both Root and Shizuku), and retrieving application information.

    To use it, instantiate ShizukuHostApi. You can optionally provide a BinaryMessenger for dependency injection or a messageChannelSuffix if you are using multiple instances or custom channels.

    final api = ShizukuHostApi();
    
    // Example: Check if Shizuku permission is granted
    bool hasPermission = await api.checkPermission();
    
    // Example: Run a shell command
    CommandRequest request = CommandRequest(command: 'ls', mode: '-l');
    CommandResult result = await api.runCommand(request);
    print(result.output);
  12. Use CommandOutputHighlightedText to display searchable command output

    main

    The CommandOutputHighlightedText widget is used to render command output text with support for search highlighting and automatic scrolling to the current match. It uses a monospace font and supports both vertical and horizontal scrolling to ensure long lines or large outputs are navigable.

    Key Features

    • Search Highlighting: Matches are highlighted with Colors.yellowAccent. The currently selected match (currentMatchIndex) is highlighted with Colors.orangeAccent and a bolder font weight.
    • Auto-Scrolling: When the currentMatchIndex or the search query changes, the widget automatically animates the scrollController to bring the active match into view.
    • Customizable Styling: You can control fontSize, textColor, and backgroundColor.
    • Padding Control: Use scrollTopPadding and scrollLeftPadding to adjust the offset when the widget scrolls to a match.
    CommandOutputHighlightedText(
      text: commandOutputString,
      searchQuery: currentSearchQuery,
      searchMatches: listOfMatchStartIndices,
      currentMatchIndex: activeMatchIndex,
      fontSize: 14.0,
      textColor: Colors.white,
      backgroundColor: Colors.black,
      scrollController: myScrollController,
      scrollTopPadding: 32,
      scrollLeftPadding: 24,
    );