AndroidFridaManager 1.9.4__py3-none-any.whl → 1.9.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,4 +2,4 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  __author__ = "Daniel Baier"
5
- __version__ = "1.9.4"
5
+ __version__ = "1.9.5"
@@ -134,15 +134,38 @@ class Job:
134
134
  raise FridaBasedException("Connection is closed. Probably the target app crashed")
135
135
 
136
136
 
137
- def close_job(self):
137
+ def close_job(self, timeout: float = 5.0) -> bool:
138
+ """Stop the job and cleanup resources.
139
+
140
+ Args:
141
+ timeout: Maximum seconds to wait for thread to stop.
142
+ Default 5.0 seconds. Use 0 for no wait.
143
+
144
+ Returns:
145
+ True if job stopped cleanly, False if timed out.
146
+ """
138
147
  self.state = "stopping"
139
148
  self.stop_event.set()
140
- if self.thread:
141
- self.thread.join()
149
+
150
+ timed_out = False
151
+ if self.thread and self.thread.is_alive():
152
+ self.thread.join(timeout=timeout if timeout > 0 else None)
153
+ if self.thread.is_alive():
154
+ self.logger.warning(
155
+ f"Job {self.job_id} thread did not stop within {timeout}s"
156
+ )
157
+ timed_out = True
158
+
159
+ # Try to unload script even if thread timed out
142
160
  if self.script:
143
- self.script.unload()
144
-
145
- self.logger.info(f"Job {self.job_id} stopped")
161
+ try:
162
+ self.script.unload()
163
+ except Exception as e:
164
+ self.logger.warning(f"Error unloading script: {e}")
165
+
166
+ status = "timed out" if timed_out else "stopped"
167
+ self.logger.info(f"Job {self.job_id} {status}")
168
+ return not timed_out
146
169
 
147
170
 
148
171
  def get_id(self):
@@ -424,29 +424,48 @@ class JobManager(object):
424
424
  return None
425
425
 
426
426
 
427
- def stop_jobs(self):
427
+ def stop_jobs(self, timeout_per_job: float = 3.0) -> dict:
428
+ """Stop all running jobs.
429
+
430
+ Args:
431
+ timeout_per_job: Maximum seconds to wait per job.
432
+
433
+ Returns:
434
+ Dictionary mapping job_id to success status.
435
+ """
436
+ results = {}
428
437
  jobs_to_stop = [job_id for job_id, job in self.jobs.items() if job.state == "running"]
438
+
429
439
  for job_id in jobs_to_stop:
430
440
  try:
431
- self.logger.info('[job manager] Job: {0} - Stopping'.format(job_id))
432
- self.stop_job_with_id(job_id)
441
+ self.logger.info(f'[job manager] Job: {job_id} - Stopping')
442
+ results[job_id] = self.stop_job_with_id(job_id, timeout=timeout_per_job)
433
443
  except frida.InvalidOperationError:
434
- self.logger.error('[job manager] Job: {0} - An error occurred stopping job. Device may '
435
- 'no longer be available.'.format(job_id))
444
+ self.logger.error(f'[job manager] Job: {job_id} - Error stopping')
445
+ results[job_id] = False
446
+
447
+ return results
436
448
 
437
449
 
438
- def stop_job_with_id(self, job_id: str) -> None:
450
+ def stop_job_with_id(self, job_id: str, timeout: float = 5.0) -> bool:
439
451
  """Stop a specific job by ID.
440
452
 
441
453
  Args:
442
454
  job_id: UUID of the job to stop.
455
+ timeout: Maximum seconds to wait for job to stop.
456
+
457
+ Returns:
458
+ True if job stopped cleanly, False if timed out or not found.
443
459
  """
444
- if job_id in self.jobs:
445
- job = self.jobs[job_id]
446
- # Unregister hooks before closing
447
- self.unregister_hooks(job_id)
448
- job.close_job()
449
- del self.jobs[job_id]
460
+ if job_id not in self.jobs:
461
+ return False
462
+
463
+ job = self.jobs[job_id]
464
+ # Unregister hooks before closing
465
+ self.unregister_hooks(job_id)
466
+ success = job.close_job(timeout=timeout)
467
+ del self.jobs[job_id]
468
+ return success
450
469
 
451
470
 
452
471
  def get_last_created_job(self):
@@ -461,10 +480,38 @@ class JobManager(object):
461
480
  raise ValueError(f"Job with ID {job_id} not found.")
462
481
 
463
482
 
464
- def detach_from_app(self):
465
- if self.process_session:
483
+ def detach_from_app(self, timeout: float = 3.0) -> bool:
484
+ """Detach from the current app session.
485
+
486
+ Args:
487
+ timeout: Maximum seconds to wait for detach.
488
+
489
+ Returns:
490
+ True if detached successfully, False if timed out or failed.
491
+ """
492
+ if not self.process_session:
493
+ return True
494
+
495
+ import concurrent.futures
496
+
497
+ def _detach():
466
498
  self.process_session.detach()
467
499
 
500
+ try:
501
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
502
+ future = executor.submit(_detach)
503
+ future.result(timeout=timeout)
504
+ self.process_session = None
505
+ return True
506
+ except concurrent.futures.TimeoutError:
507
+ self.logger.warning(f"Detach timed out after {timeout}s")
508
+ self.process_session = None # Clear anyway to avoid reuse
509
+ return False
510
+ except Exception as e:
511
+ self.logger.warning(f"Detach failed: {e}")
512
+ self.process_session = None
513
+ return False
514
+
468
515
 
469
516
  def stop_app(self, app_package):
470
517
  cmd = self._build_adb_command(["shell", "am", "force-stop", app_package])
@@ -670,23 +717,21 @@ class JobManager(object):
670
717
  """Get the current session mode ('spawn' or 'attach')."""
671
718
  return self._mode
672
719
 
673
- def reset_session(self) -> None:
720
+ def reset_session(self, timeout_per_job: float = 2.0, detach_timeout: float = 2.0) -> None:
674
721
  """Reset the session state for a new connection.
675
722
 
676
- Stops all jobs, clears hook registry, and resets session state.
723
+ Args:
724
+ timeout_per_job: Max seconds to wait per job when stopping.
725
+ detach_timeout: Max seconds to wait for session detach.
677
726
  """
678
- # Stop all running jobs
679
- self.stop_jobs()
727
+ # Stop all running jobs with timeout
728
+ self.stop_jobs(timeout_per_job=timeout_per_job)
680
729
 
681
730
  # Clear hook registry
682
731
  self._hook_registry.clear()
683
732
 
684
- # Detach from app
685
- if self.process_session:
686
- try:
687
- self.process_session.detach()
688
- except Exception:
689
- pass
733
+ # Detach from app with timeout
734
+ self.detach_from_app(timeout=detach_timeout)
690
735
 
691
736
  # Reset state
692
737
  self.process_session = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AndroidFridaManager
3
- Version: 1.9.4
3
+ Version: 1.9.5
4
4
  Summary: A python API in order to install and run the frida-server on an Android device.
5
5
  Home-page: https://github.com/fkie-cad/AndroidFridaManager
6
6
  Author: Daniel Baier
@@ -33,7 +33,7 @@ Dynamic: requires-dist
33
33
  Dynamic: requires-python
34
34
  Dynamic: summary
35
35
 
36
- ![version](https://img.shields.io/badge/version-1.9.4-blue) [![PyPI version](https://badge.fury.io/py/AndroidFridaManager.svg)](https://badge.fury.io/py/AndroidFridaManager) [![Publish status](https://github.com/fkie-cad/friTap/actions/workflows/publish.yml/badge.svg?branch=main)](https://github.com/fkie-cad/AndroidFridaManager/actions/workflows/publish-to-pypi.yml)
36
+ ![version](https://img.shields.io/badge/version-1.9.5-blue) [![PyPI version](https://badge.fury.io/py/AndroidFridaManager.svg)](https://badge.fury.io/py/AndroidFridaManager) [![Publish status](https://github.com/fkie-cad/friTap/actions/workflows/publish.yml/badge.svg?branch=main)](https://github.com/fkie-cad/AndroidFridaManager/actions/workflows/publish-to-pypi.yml)
37
37
 
38
38
  # AndroidFridaManager
39
39
 
@@ -0,0 +1,11 @@
1
+ AndroidFridaManager/FridaManager.py,sha256=pTcis4oLPBN5U7CdBqdUufwuG6QQDWTPBleTC7rtTbI,32145
2
+ AndroidFridaManager/__init__.py,sha256=T6AKtrGSLQ9M5bJoWDQcsRTJbSEbksdgrx3AAAdozRI,171
3
+ AndroidFridaManager/about.py,sha256=wGo_88YOuzXo2vF1EyCEmIO4uxM7wOJLC-FrGLp__Mw,98
4
+ AndroidFridaManager/job.py,sha256=Yzir0XkkpFj5cFIcMRGJlLnIcIuDVZ9jBLFMqwQKTFA,7041
5
+ AndroidFridaManager/job_manager.py,sha256=Ce2lHLezRVKuj7r_RodH6Q8l43zL_cL5ZGhE1zGB-E8,27336
6
+ androidfridamanager-1.9.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ androidfridamanager-1.9.5.dist-info/METADATA,sha256=pjOCtO1KPNhyybRz8O5a56LXfeTqnKRcm0P0Kii5kpA,5141
8
+ androidfridamanager-1.9.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ androidfridamanager-1.9.5.dist-info/entry_points.txt,sha256=GmNngu2fDNCxUcquFRegBa7GWknPKG1jsM4lvWeyKnY,64
10
+ androidfridamanager-1.9.5.dist-info/top_level.txt,sha256=oH2lVMSRlghmt-_tVrOEUqvY462P9hd5Ktgp5-1qF3o,20
11
+ androidfridamanager-1.9.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- AndroidFridaManager/FridaManager.py,sha256=pTcis4oLPBN5U7CdBqdUufwuG6QQDWTPBleTC7rtTbI,32145
2
- AndroidFridaManager/__init__.py,sha256=T6AKtrGSLQ9M5bJoWDQcsRTJbSEbksdgrx3AAAdozRI,171
3
- AndroidFridaManager/about.py,sha256=st5zrhGhtBpwZErvuVGrS-VQL9AmtAxFwL9AYgf-rqI,98
4
- AndroidFridaManager/job.py,sha256=sMkTk1rHdfNmh9jFNQuiL4oqsUEBCJcbP6p2DZQPbL0,6172
5
- AndroidFridaManager/job_manager.py,sha256=oPkzFK8XpwFhDHKuJmAoqylUDaV6VIKL_AikJBcg3iQ,25772
6
- androidfridamanager-1.9.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
- androidfridamanager-1.9.4.dist-info/METADATA,sha256=bghFYQPx8KRnUXKXZNyfHkPmBuTx3yBjT5_jXSZD9HM,5141
8
- androidfridamanager-1.9.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- androidfridamanager-1.9.4.dist-info/entry_points.txt,sha256=GmNngu2fDNCxUcquFRegBa7GWknPKG1jsM4lvWeyKnY,64
10
- androidfridamanager-1.9.4.dist-info/top_level.txt,sha256=oH2lVMSRlghmt-_tVrOEUqvY462P9hd5Ktgp5-1qF3o,20
11
- androidfridamanager-1.9.4.dist-info/RECORD,,