Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bab62495 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Make change and version bump to BP1A.241208.001

Snap for 12765415 from 1eabb056 to 25Q1-release

Change-Id: Ieb321d35045c24bfa3e8086c8c706b05cac58fff
parents 392e17fe 1eabb056
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -18,4 +18,4 @@
# (like "CRB01").  It must be a single word, and is
# capitalized by convention.

BUILD_ID=BP1A.241207.001
BUILD_ID=BP1A.241208.001
+41 −17
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@
# limitations under the License.


import errno
import fcntl
import getpass
import hashlib
import logging
@@ -100,16 +102,32 @@ class DaemonManager:
      logging.warning("Edit monitor for cog is not supported, exiting...")
      return

    setup_lock_file = pathlib.Path(tempfile.gettempdir()).joinpath(
        self.pid_file_path.name + ".setup"
    )
    logging.info("setup lock file: %s", setup_lock_file)
    with open(setup_lock_file, "w") as f:
      try:
        # Acquire an exclusive lock
        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
        self._stop_any_existing_instance()
        self._write_pid_to_pidfile()
        self._start_daemon_process()
      except Exception as e:
        if (
            isinstance(e, IOError) and e.errno == errno.EAGAIN
        ):  # Failed to acquire the file lock.
          logging.warning("Another edit monitor is starting, exitinng...")
          return
        else:
          logging.exception("Failed to start daemon manager with error %s", e)
          self._send_error_event_to_clearcut(
              edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR
          )
          raise e
      finally:
        # Release the lock
        fcntl.flock(f, fcntl.LOCK_UN)

  def monitor_daemon(
      self,
@@ -413,13 +431,19 @@ class DaemonManager:
  def _find_all_instances_pids(self) -> list[int]:
    pids = []

    for file in os.listdir(self.pid_file_path.parent):
      if file.endswith(".lock"):
    try:
          with open(self.pid_file_path.parent.joinpath(file), "r") as f:
            pids.append(int(f.read().strip()))
        except (FileNotFoundError, IOError, ValueError, TypeError):
          logging.exception("Failed to get pid from file path: %s", file)
      output = subprocess.check_output(["ps", "-ef", "--no-headers"], text=True)
      for line in output.splitlines():
        parts = line.split()
        process_path = parts[7]
        if pathlib.Path(process_path).name == "edit_monitor":
          pid = int(parts[1])
          if pid != self.pid:  # exclude the current process
            pids.append(pid)
    except Exception:
      logging.exception(
          "Failed to get pids of existing edit monitors from ps command."
      )

    return pids

+50 −5
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

"""Unittests for DaemonManager."""

import fcntl
import logging
import multiprocessing
import os
@@ -82,7 +83,8 @@ class DaemonManagerTest(unittest.TestCase):
    # tests will be cleaned.
    tempfile.tempdir = self.working_dir.name
    self.patch = mock.patch.dict(
        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'})
        os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'}
    )
    self.patch.start()

  def tearDown(self):
@@ -102,6 +104,7 @@ class DaemonManagerTest(unittest.TestCase):
    p = self._create_fake_deamon_process()

    self.assert_run_simple_daemon_success()
    self.assert_no_subprocess_running()

  def test_start_success_with_existing_instance_already_dead(self):
    # Create a pidfile with pid that does not exist.
@@ -137,7 +140,9 @@ class DaemonManagerTest(unittest.TestCase):
    # Verify no daemon process is started.
    self.assertIsNone(dm.daemon_process)

  @mock.patch.dict(os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True)
  @mock.patch.dict(
      os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True
  )
  def test_start_return_directly_if_disabled(self):
    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
    dm.start()
@@ -154,6 +159,25 @@ class DaemonManagerTest(unittest.TestCase):
    # Verify no daemon process is started.
    self.assertIsNone(dm.daemon_process)

  def test_start_failed_other_instance_is_starting(self):
    f = open(
        pathlib.Path(self.working_dir.name).joinpath(
            TEST_PID_FILE_PATH + '.setup'
        ),
        'w',
    )
    # Acquire an exclusive lock
    fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)

    dm = daemon_manager.DaemonManager(TEST_BINARY_FILE)
    dm.start()

    # Release the lock
    fcntl.flock(f, fcntl.LOCK_UN)
    f.close()
    # Verify no daemon process is started.
    self.assertIsNone(dm.daemon_process)

  @mock.patch('os.kill')
  def test_start_failed_to_kill_existing_instance(self, mock_kill):
    mock_kill.side_effect = OSError('Unknown OSError')
@@ -177,6 +201,7 @@ class DaemonManagerTest(unittest.TestCase):
        'edit_monitor'
    )
    pid_file_path_dir.mkdir(parents=True, exist_ok=True)

    # Makes the directory read-only so write pidfile will fail.
    os.chmod(pid_file_path_dir, 0o555)

@@ -367,6 +392,26 @@ class DaemonManagerTest(unittest.TestCase):
        fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR
    )

  @mock.patch('subprocess.check_output')
  def test_cleanup_success(self, mock_check_output):
    p = self._create_fake_deamon_process()
    fake_cclient = FakeClearcutClient()
    mock_check_output.return_value = f'user {p.pid} 1 1 1 1 1 edit_monitor arg'

    dm = daemon_manager.DaemonManager(
        TEST_BINARY_FILE,
        daemon_target=long_running_daemon,
        cclient=fake_cclient,
    )
    dm.cleanup()

    self.assertFalse(p.is_alive())
    self.assertTrue(
        pathlib.Path(self.working_dir.name)
        .joinpath(daemon_manager.BLOCK_SIGN_FILE)
        .exists()
    )

  def assert_run_simple_daemon_success(self):
    damone_output_file = tempfile.NamedTemporaryFile(
        dir=self.working_dir.name, delete=False
@@ -432,7 +477,7 @@ class DaemonManagerTest(unittest.TestCase):
        pass

  def _create_fake_deamon_process(
      self, name: str = ''
      self, name: str = TEST_PID_FILE_PATH
  ) -> multiprocessing.Process:
    # Create a long running subprocess
    p = multiprocessing.Process(target=long_running_daemon)
@@ -443,7 +488,7 @@ class DaemonManagerTest(unittest.TestCase):
        'edit_monitor'
    )
    pid_file_path_dir.mkdir(parents=True, exist_ok=True)
    with open(pid_file_path_dir.joinpath(name + 'pid.lock'), 'w') as f:
    with open(pid_file_path_dir.joinpath(name), 'w') as f:
      f.write(str(p.pid))
    return p

+5 −5
Original line number Diff line number Diff line
@@ -102,10 +102,10 @@ def main(argv: list[str]):
      daemon_args=(args.path, args.dry_run),
  )

  try:
    if args.force_cleanup:
      dm.cleanup()

  try:
    else:
      dm.start()
      dm.monitor_daemon()
  except Exception: