新增模块覆盖: 系统层 (14): system, security, print, opengl, renderscript, transition, test, service_notification, service_controls, service_quicksettings, service_wallpaper, service_autofill, service_dreams, service_vr 应用层 (13): app_admin, app_backup, app_job, app_usage, app_role, app_people, appwidget, companion, credentials, mtp, sip, telecom, speech 硬件媒体层 (15): hardware_display, hardware_fingerprint, hardware_usb, hardware_input, media_audiofx, media_browse, media_session, media_tv, media_midi, media_projection, uwb, se, view_autofill, view_textclassifier, view_translation 总计 94 个模块,75,342 行代码
444 lines
15 KiB
Python
444 lines
15 KiB
Python
"""
|
|
Android Job Scheduler API — JobScheduler, JobInfo, JobService, JobParameters, JobWorkItem
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import heapq
|
|
import threading
|
|
import time
|
|
from datetime import datetime, timedelta
|
|
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
__all__ = [
|
|
"JobScheduler",
|
|
"JobInfo",
|
|
"JobService",
|
|
"JobParameters",
|
|
"JobWorkItem",
|
|
]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# JobScheduler
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class JobScheduler(Stub):
|
|
"""JobScheduler — 作业调度器 (桌面线程模拟)"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._lock = threading.Lock()
|
|
self._jobs: Dict[int, "JobInfo"] = {}
|
|
self._next_id = 1
|
|
self._worker_thread: Optional[threading.Thread] = None
|
|
self._running = False
|
|
self._start_worker()
|
|
|
|
def _start_worker(self):
|
|
"""启动后台工作线程"""
|
|
if self._worker_thread is None or not self._worker_thread.is_alive():
|
|
self._running = True
|
|
self._worker_thread = threading.Thread(target=self._run_loop, daemon=True)
|
|
self._worker_thread.start()
|
|
|
|
def _run_loop(self):
|
|
"""运行作业调度循环"""
|
|
while self._running:
|
|
with self._lock:
|
|
now = time.time()
|
|
for job_id, info in list(self._jobs.items()):
|
|
if info._deadline and now >= info._deadline:
|
|
self._jobs.pop(job_id, None)
|
|
|
|
time.sleep(1.0)
|
|
|
|
def schedule(self, job: "JobInfo") -> int:
|
|
"""调度一个作业"""
|
|
with self._lock:
|
|
job_id = self._next_id
|
|
self._next_id += 1
|
|
job._id = job_id
|
|
if job._periodic:
|
|
job._deadline = time.time() + job._period_ms / 1000.0
|
|
elif job._deadline_ms:
|
|
job._deadline = time.time() + job._deadline_ms / 1000.0
|
|
else:
|
|
job._deadline = None
|
|
self._jobs[job_id] = job
|
|
return job_id
|
|
|
|
def enqueue(self, job: "JobInfo", workItem: "JobWorkItem") -> int:
|
|
"""将作业加入队列"""
|
|
return self.schedule(job)
|
|
|
|
def cancel(self, jobId: int):
|
|
"""取消指定作业"""
|
|
with self._lock:
|
|
self._jobs.pop(jobId, None)
|
|
|
|
def cancelAll(self):
|
|
"""取消所有作业"""
|
|
with self._lock:
|
|
self._jobs.clear()
|
|
|
|
def getAllPendingJobs(self) -> List["JobInfo"]:
|
|
"""获取所有待处理作业"""
|
|
with self._lock:
|
|
return list(self._jobs.values())
|
|
|
|
def getPendingJob(self, jobId: int) -> Optional["JobInfo"]:
|
|
"""获取指定待处理作业"""
|
|
with self._lock:
|
|
return self._jobs.get(jobId)
|
|
|
|
def getTransientNetwork(self) -> Optional[Any]:
|
|
"""获取瞬时网络"""
|
|
return None
|
|
|
|
def getStartedJobs(self) -> List["JobInfo"]:
|
|
"""获取已开始的作业"""
|
|
return []
|
|
|
|
def toString(self) -> str:
|
|
with self._lock:
|
|
return f"JobScheduler(pending={len(self._jobs)})"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# JobInfo
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class JobInfo(Stub):
|
|
"""JobInfo — 作业信息"""
|
|
|
|
# --- NetworkType 常量 ---
|
|
NETWORK_TYPE_NONE = 0
|
|
NETWORK_TYPE_ANY = 1
|
|
NETWORK_TYPE_UNMETERED = 2
|
|
NETWORK_TYPE_NOT_ROAMING = 3
|
|
NETWORK_TYPE_CELLULAR = 4
|
|
|
|
# --- Backoff 策略 ---
|
|
BACKOFF_POLICY_LINEAR = 0
|
|
BACKOFF_POLICY_EXPONENTIAL = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._id = 0
|
|
self._service = None
|
|
self._required_network_type = self.NETWORK_TYPE_NONE
|
|
self._requires_charging = False
|
|
self._requires_device_idle = False
|
|
self._deadline_ms = 0
|
|
self._min_latency_ms = 0
|
|
self._period_ms = 0
|
|
self._periodic = False
|
|
self._backoff_policy = self.BACKOFF_POLICY_EXPONENTIAL
|
|
self._backoff_delay_ms = 30000
|
|
self._extras = None
|
|
self._transient_network = False
|
|
self._estimated_network_bytes = -1
|
|
self._prefetch_network_bytes = -1
|
|
self._requires_storage_not_low = False
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getService(self) -> Optional[Any]:
|
|
return self._service
|
|
|
|
def getRequiredNetworkType(self) -> int:
|
|
return self._required_network_type
|
|
|
|
def isRequiresCharging(self) -> bool:
|
|
return self._requires_charging
|
|
|
|
def isRequiresDeviceIdle(self) -> bool:
|
|
return self._requires_device_idle
|
|
|
|
def isRequiresStorageNotLow(self) -> bool:
|
|
return self._requires_storage_not_low
|
|
|
|
def getMinLatencyMillis(self) -> int:
|
|
return self._min_latency_ms
|
|
|
|
def getMaxExecutionDelayMillis(self) -> int:
|
|
return self._deadline_ms
|
|
|
|
def isPeriodic(self) -> bool:
|
|
return self._periodic
|
|
|
|
def getBackoffPolicy(self) -> int:
|
|
return self._backoff_policy
|
|
|
|
def getInitialBackoffMillis(self) -> int:
|
|
return self._backoff_delay_ms
|
|
|
|
def getEstimatedNetworkBytes(self) -> int:
|
|
return self._estimated_network_bytes
|
|
|
|
def getPrefetchNetworkBytes(self) -> int:
|
|
return self._prefetch_network_bytes
|
|
|
|
def getTransientNetwork(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def isPersisted(self) -> bool:
|
|
return False
|
|
|
|
def getExtras(self) -> Optional[Any]:
|
|
return self._extras
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# Builder
|
|
# ═══════════════════════════════════════════════════════════
|
|
|
|
class Builder(Stub):
|
|
"""JobInfo.Builder — 作业构建器"""
|
|
|
|
def __init__(self, jobId: int, jobService: Any):
|
|
super().__init__()
|
|
self._info = JobInfo()
|
|
self._info._id = jobId
|
|
self._info._service = jobService
|
|
|
|
def setRequiredNetworkType(self, networkType: int) -> "JobInfo.Builder":
|
|
self._info._required_network_type = networkType
|
|
return self
|
|
|
|
def setRequiresCharging(self, requiresCharging: bool) -> "JobInfo.Builder":
|
|
self._info._requires_charging = requiresCharging
|
|
return self
|
|
|
|
def setRequiresDeviceIdle(self, requiresDeviceIdle: bool) -> "JobInfo.Builder":
|
|
self._info._requires_device_idle = requiresDeviceIdle
|
|
return self
|
|
|
|
def setRequiresStorageNotLow(self, requiresStorageNotLow: bool) -> "JobInfo.Builder":
|
|
self._info._requires_storage_not_low = requiresStorageNotLow
|
|
return self
|
|
|
|
def setOverrideDeadline(self, deadlineMillis: int) -> "JobInfo.Builder":
|
|
self._info._deadline_ms = deadlineMillis
|
|
return self
|
|
|
|
def setMinimumLatency(self, minLatencyMillis: int) -> "JobInfo.Builder":
|
|
self._info._min_latency_ms = minLatencyMillis
|
|
return self
|
|
|
|
def setBackoffCriteria(
|
|
self, initialBackoffMillis: int, backoffPolicy: int
|
|
) -> "JobInfo.Builder":
|
|
self._info._backoff_delay_ms = initialBackoffMillis
|
|
self._info._backoff_policy = backoffPolicy
|
|
return self
|
|
|
|
def setPeriodic(self, intervalMillis: int) -> "JobInfo.Builder":
|
|
self._info._periodic = True
|
|
self._info._period_ms = intervalMillis
|
|
return self
|
|
|
|
def setTransientNetwork(self, transient: bool) -> "JobInfo.Builder":
|
|
self._info._transient_network = transient
|
|
return self
|
|
|
|
def setEstimatedNetworkBytes(self, estimatedBytes: int) -> "JobInfo.Builder":
|
|
self._info._estimated_network_bytes = estimatedBytes
|
|
return self
|
|
|
|
def setPrefetchNetworkBytes(self, prefetchBytes: int) -> "JobInfo.Builder":
|
|
self._info._prefetch_network_bytes = prefetchBytes
|
|
return self
|
|
|
|
def setExtras(self, extras: Any) -> "JobInfo.Builder":
|
|
self._info._extras = extras
|
|
return self
|
|
|
|
def build(self) -> "JobInfo":
|
|
return self._info
|
|
|
|
def toString(self) -> str:
|
|
return f"Builder(jobId={self._info._id})"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"JobInfo(id={self._id}, network={self._required_network_type})"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, JobInfo):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# JobService
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class JobService(Stub):
|
|
"""JobService — 作业服务基类"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def onStartJob(self, params: "JobParameters") -> bool:
|
|
"""作业开始时调用。返回 True 表示需要后台线程处理。"""
|
|
return False
|
|
|
|
def onStopJob(self, params: "JobParameters") -> bool:
|
|
"""作业停止时调用。返回 True 表示应重试。"""
|
|
return False
|
|
|
|
def jobFinished(self, params: "JobParameters", needsReschedule: bool):
|
|
"""通知系统作业已完成"""
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"JobService@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# JobParameters
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class JobParameters(Stub):
|
|
"""JobParameters — 作业参数"""
|
|
|
|
# --- StopReason 常量 ---
|
|
STOP_REASON_UNDEFINED = 0
|
|
STOP_REASON_DATA_CLEARED = 1
|
|
STOP_REASON_APP_BACKGROUND = 2
|
|
STOP_REASON_APP_STANDBY = 3
|
|
STOP_REASON_DEVICE_IDLE = 4
|
|
STOP_REASON_PREEXISTING = 5
|
|
STOP_REASON_ESTIMATED_NETWORK_BYTES_EXCEEDED = 6
|
|
STOP_REASON_QUOTA = 7
|
|
STOP_REASON_TAPPED_CANCEL = 8
|
|
STOP_REASON_USER = 9
|
|
STOP_REASON_CONSTRAINT_BATTERY_NOT_OK = 10
|
|
STOP_REASON_CONSTRAINT_NETWORK_NOT_OK = 11
|
|
STOP_REASON_CONSTRAINT_CONNECTIVITY_NOT_OK = 12
|
|
STOP_REASON_CONSTRAINT_STORAGE_NOT_OK = 13
|
|
STOP_REASON_CONSTRAINT_CHARGING_NOT_OK = 14
|
|
STOP_REASON_CONSTRAINT_IDLE_NOT_OK = 15
|
|
STOP_REASON_CANCELED = 16
|
|
STOP_REASON_CANCELLED_BY_APP = 17
|
|
STOP_REASON_DEVICE_STATE = 18
|
|
STOP_REASON_SYSTEM_PROCESSING = 19
|
|
STOP_REASON_WORK_POLICY = 20
|
|
STOP_REASON_SYSTEM_EXECUTION = 21
|
|
STOP_REASON_RESTRICTED_BACKGROUND = 22
|
|
STOP_REASON_TIMEOUT = 23
|
|
|
|
def __init__(self, jobId: int = 0):
|
|
super().__init__()
|
|
self._job_id = jobId
|
|
self._extras = None
|
|
self._network = None
|
|
self._transient_network = None
|
|
self._stop_reason = 0
|
|
self._override_deadline_expired = False
|
|
self._requires_charging = False
|
|
self._requires_device_idle = False
|
|
self._requires_storage_not_low = False
|
|
self._required_network_type = JobInfo.NETWORK_TYPE_NONE
|
|
|
|
def getJobId(self) -> int:
|
|
return self._job_id
|
|
|
|
def getExtras(self) -> Optional[Any]:
|
|
return self._extras
|
|
|
|
def getNetwork(self) -> Optional[Any]:
|
|
return self._network
|
|
|
|
def getTransientNetwork(self) -> Optional[Any]:
|
|
return self._transient_network
|
|
|
|
def getStopReason(self) -> int:
|
|
return self._stop_reason
|
|
|
|
def isOverrideDeadlineExpired(self) -> bool:
|
|
return self._override_deadline_expired
|
|
|
|
def isRequiresCharging(self) -> bool:
|
|
return self._requires_charging
|
|
|
|
def isRequiresDeviceIdle(self) -> bool:
|
|
return self._requires_device_idle
|
|
|
|
def isRequiresStorageNotLow(self) -> bool:
|
|
return self._requires_storage_not_low
|
|
|
|
def getRequiredNetworkType(self) -> int:
|
|
return self._required_network_type
|
|
|
|
def toString(self) -> str:
|
|
return f"JobParameters(jobId={self._job_id})"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, JobParameters):
|
|
return False
|
|
return self._job_id == other._job_id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._job_id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# JobWorkItem
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class JobWorkItem(Stub):
|
|
"""JobWorkItem — 作业工作项"""
|
|
|
|
def __init__(self, intent: Any = None):
|
|
super().__init__()
|
|
self._intent = intent
|
|
self._delivery_count = 0
|
|
self._latest_delivery_intent = None
|
|
|
|
def getIntent(self) -> Optional[Any]:
|
|
return self._intent
|
|
|
|
def getDeliveryCount(self) -> int:
|
|
return self._delivery_count
|
|
|
|
def getLatestDeliveryIntent(self) -> Optional[Any]:
|
|
return self._latest_delivery_intent
|
|
|
|
def toString(self) -> str:
|
|
return f"JobWorkItem(deliveryCount={self._delivery_count})"
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, JobWorkItem):
|
|
return False
|
|
return self._intent == other._intent
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(str(self._intent)) & 0x7FFFFFFF |