- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
456 lines
18 KiB
Python
456 lines
18 KiB
Python
"""
|
|
Android accounts 组件 — AccountManager, Account, AuthenticatorDescription 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import threading
|
|
import uuid
|
|
from typing import Any, Callable, Dict, List, Optional, Set
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Account
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Account(Stub):
|
|
"""Account — 账户"""
|
|
|
|
def __init__(self, name: str, type_: str):
|
|
super().__init__()
|
|
self.name: str = name
|
|
self.type: str = type_
|
|
self._extras: Dict[str, Any] = {}
|
|
|
|
def toString(self) -> str:
|
|
return f"Account{{name={self.name}, type={self.type}}}"
|
|
|
|
def describeContents(self) -> int:
|
|
return 0
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self.name, self.type)) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, Account):
|
|
return False
|
|
return self.name == other.name and self.type == other.type
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AccountManagerFuture
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AccountManagerFuture(Stub):
|
|
"""AccountManagerFuture — 异步结果"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._result: Any = None
|
|
self._exception: Optional[Exception] = None
|
|
self._done: bool = False
|
|
self._event = threading.Event()
|
|
|
|
def getResult(self) -> Any:
|
|
self._event.wait()
|
|
if self._exception:
|
|
raise self._exception
|
|
return self._result
|
|
|
|
def getException(self) -> Optional[Exception]:
|
|
return self._exception
|
|
|
|
def isDone(self) -> bool:
|
|
return self._done
|
|
|
|
def isCancelled(self) -> bool:
|
|
return False
|
|
|
|
def cancel(self, mayInterruptIfRunning: bool) -> bool:
|
|
return False
|
|
|
|
def _set_result(self, result: Any):
|
|
self._result = result
|
|
self._done = True
|
|
self._event.set()
|
|
|
|
def _set_exception(self, exc: Exception):
|
|
self._exception = exc
|
|
self._done = True
|
|
self._event.set()
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AccountManagerCallback
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AccountManagerCallback(Stub):
|
|
"""AccountManagerCallback — 回调接口"""
|
|
|
|
def run(self, future: AccountManagerFuture):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AuthenticatorDescription
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AuthenticatorDescription(Stub):
|
|
"""AuthenticatorDescription — 认证器描述"""
|
|
|
|
KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse"
|
|
|
|
def __init__(self, type_: str, packageName: str = "",
|
|
labelId: int = 0, iconId: int = 0,
|
|
smallIconId: int = 0, accountPreferencesId: int = 0,
|
|
customTokens: bool = False, prefMinId: int = 0):
|
|
super().__init__()
|
|
self.type: str = type_
|
|
self.packageName: str = packageName
|
|
self.labelId: int = labelId
|
|
self.iconId: int = iconId
|
|
self.smallIconId: int = smallIconId
|
|
self.accountPreferencesId: int = accountPreferencesId
|
|
self.customTokens: bool = customTokens
|
|
self.prefMinId: int = prefMinId
|
|
|
|
def toString(self) -> str:
|
|
return f"AuthenticatorDescription{{type={self.type}}}"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self.type) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, AuthenticatorDescription):
|
|
return False
|
|
return self.type == other.type
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# OnAccountsUpdateListener
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class OnAccountsUpdateListener(Stub):
|
|
"""OnAccountsUpdateListener — 账户更新监听器"""
|
|
|
|
def onAccountsUpdated(self, accounts: Optional[List[Account]]):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AccountManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AccountManager(Stub):
|
|
"""AccountManager — 账户管理器"""
|
|
|
|
ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED"
|
|
ACTION_VISIBLE_ACCOUNTS_CHANGED = "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED"
|
|
KEY_ACCOUNTS = "accounts"
|
|
KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse"
|
|
KEY_ACCOUNT_TYPE = "accountType"
|
|
KEY_AUTHTOKEN = "authtoken"
|
|
KEY_AUTH_FAILED_MESSAGE = "authFailedMessage"
|
|
KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey"
|
|
KEY_BOOLEAN_RESULT = "booleanResult"
|
|
KEY_ERROR_CODE = "errorCode"
|
|
KEY_ERROR_MESSAGE = "errorMessage"
|
|
KEY_INTENT = "intent"
|
|
KEY_LAST_SEEN_PIN = "lastSeenPin"
|
|
KEY_PASSWORD = "password"
|
|
KEY_USERDATA = "userdata"
|
|
LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED"
|
|
PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible"
|
|
PACKAGE_NAME_KEY_LEGACY_VISIBLE = "android.accounts.legacy_visible"
|
|
PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible"
|
|
|
|
ERROR_CODE_REMOTE_EXCEPTION = 1
|
|
ERROR_CODE_NETWORK_ERROR = 2
|
|
ERROR_CODE_CANCELED = 3
|
|
ERROR_CODE_INVALID_RESPONSE = 4
|
|
ERROR_CODE_UNSUPPORTED_OPERATION = 5
|
|
ERROR_CODE_BAD_ARGUMENTS = 6
|
|
ERROR_CODE_BAD_AUTHENTICATION = 7
|
|
ERROR_CODE_BAD_REQUEST = 8
|
|
ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._lock = threading.Lock()
|
|
self._accounts: Dict[str, Account] = {}
|
|
self._passwords: Dict[str, str] = {}
|
|
self._userdata: Dict[str, Dict[str, str]] = {}
|
|
self._auth_tokens: Dict[str, Dict[str, str]] = {}
|
|
self._listeners: List[OnAccountsUpdateListener] = []
|
|
self._listener_handler: Dict[OnAccountsUpdateListener, Any] = {}
|
|
|
|
@staticmethod
|
|
def get(context: Any = None) -> "AccountManager":
|
|
return AccountManager()
|
|
|
|
# ── Account listing ──
|
|
|
|
def getAccounts(self) -> List[Account]:
|
|
with self._lock:
|
|
return list(self._accounts.values())
|
|
|
|
def getAccountsByType(self, type_: str) -> List[Account]:
|
|
with self._lock:
|
|
return [a for a in self._accounts.values() if a.type == type_]
|
|
|
|
def getAccountsByTypeAndFeatures(self, type_: str,
|
|
features: List[str],
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
accounts = self.getAccountsByType(type_)
|
|
if callback:
|
|
callback.run(future)
|
|
future._set_result(accounts)
|
|
return future
|
|
|
|
def getAccountsForPackage(self, packageName: str,
|
|
uid: int = -1) -> List[Account]:
|
|
return self.getAccounts()
|
|
|
|
def getSharedAccounts(self, userId: int) -> List[Account]:
|
|
return self.getAccounts()
|
|
|
|
# ── CRUD ──
|
|
|
|
def addAccount(self, accountType: str, authTokenType: Optional[str] = None,
|
|
requiredFeatures: Optional[List[str]] = None,
|
|
addAccountOptions: Optional[Dict[str, Any]] = None,
|
|
activity: Any = None,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
name = f"account_{uuid.uuid4().hex[:8]}"
|
|
account = Account(name, accountType)
|
|
with self._lock:
|
|
self._accounts[f"{name}:{accountType}"] = account
|
|
future._set_result(None)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
def addAccountExplicitly(self, account: Account,
|
|
password: Optional[str] = None,
|
|
userdata: Optional[Dict[str, Any]] = None) -> bool:
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
if key in self._accounts:
|
|
return False
|
|
self._accounts[key] = account
|
|
if password is not None:
|
|
self._passwords[key] = password
|
|
if userdata:
|
|
self._userdata[key] = {k: str(v) for k, v in userdata.items()}
|
|
return True
|
|
|
|
def removeAccount(self, account: Account,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
self._accounts.pop(key, None)
|
|
self._passwords.pop(key, None)
|
|
self._userdata.pop(key, None)
|
|
self._auth_tokens.pop(key, None)
|
|
future._set_result(True)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
def renameAccount(self, account: Account, newName: str) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
if key in self._accounts:
|
|
new_key = f"{newName}:{account.type}"
|
|
self._accounts[new_key] = Account(newName, account.type)
|
|
if key in self._passwords:
|
|
self._passwords[new_key] = self._passwords.pop(key)
|
|
if key in self._userdata:
|
|
self._userdata[new_key] = self._userdata.pop(key)
|
|
if key in self._auth_tokens:
|
|
self._auth_tokens[new_key] = self._auth_tokens.pop(key)
|
|
del self._accounts[key]
|
|
future._set_result(account)
|
|
return future
|
|
|
|
# ── Password ──
|
|
|
|
def getPassword(self, account: Account) -> Optional[str]:
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
return self._passwords.get(key)
|
|
|
|
def setPassword(self, account: Account, password: str):
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
if password:
|
|
self._passwords[key] = password
|
|
else:
|
|
self._passwords.pop(key, None)
|
|
|
|
def clearPassword(self, account: Account):
|
|
self.setPassword(account, "")
|
|
|
|
# ── User data ──
|
|
|
|
def getUserData(self, account: Account, key: str) -> Optional[str]:
|
|
ak = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
userdata = self._userdata.get(ak, {})
|
|
return userdata.get(key)
|
|
|
|
def setUserData(self, account: Account, key: str, value: str):
|
|
ak = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
if ak not in self._userdata:
|
|
self._userdata[ak] = {}
|
|
self._userdata[ak][key] = value
|
|
|
|
# ── Auth tokens ──
|
|
|
|
def getAuthToken(self, account: Account, authTokenType: str,
|
|
notifyAuthFailure: bool = True,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
tokens = self._auth_tokens.get(key, {})
|
|
token = tokens.get(authTokenType)
|
|
future._set_result(token)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
def peekAuthToken(self, account: Account, authTokenType: str) -> Optional[str]:
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
tokens = self._auth_tokens.get(key, {})
|
|
return tokens.get(authTokenType)
|
|
|
|
def addAuthToken(self, account: Account, authTokenType: str, authToken: str):
|
|
key = f"{account.name}:{account.type}"
|
|
with self._lock:
|
|
if key not in self._auth_tokens:
|
|
self._auth_tokens[key] = {}
|
|
self._auth_tokens[key][authTokenType] = authToken
|
|
|
|
def setAuthToken(self, account: Account, authTokenType: str, authToken: str):
|
|
self.addAuthToken(account, authTokenType, authToken)
|
|
|
|
def invalidateAuthToken(self, accountType: str, authToken: str):
|
|
with self._lock:
|
|
for key in list(self._auth_tokens.keys()):
|
|
if key.endswith(f":{accountType}"):
|
|
tokens = self._auth_tokens[key]
|
|
for ttype, token in list(tokens.items()):
|
|
if token == authToken:
|
|
del tokens[ttype]
|
|
|
|
# ── Features ──
|
|
|
|
def hasFeatures(self, account: Account, features: List[str],
|
|
callback: AccountManagerCallback, handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
future._set_result(True)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
# ── Create account ──
|
|
|
|
def createAccount(self, accountType: str, authTokenType: Optional[str] = None,
|
|
requiredFeatures: Optional[List[str]] = None,
|
|
options: Optional[Dict[str, Any]] = None,
|
|
activity: Any = None,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
return self.addAccount(accountType, authTokenType, requiredFeatures,
|
|
options, activity, callback, handler)
|
|
|
|
# ── Shared accounts ──
|
|
|
|
def removeSharedAccount(self, account: Account,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
return self.removeAccount(account, callback, handler)
|
|
|
|
def updateAppPermission(self, account: Account, authTokenType: str, uid: int, value: bool):
|
|
pass
|
|
|
|
def setAuthTokenType(self, account: Account, authTokenType: str):
|
|
pass
|
|
|
|
# ── Confirm / Edit ──
|
|
|
|
def confirmCredentials(self, account: Account,
|
|
options: Optional[Dict[str, Any]] = None,
|
|
activity: Any = None,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
future._set_result(None)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
def editProperties(self, accountType: str,
|
|
activity: Any = None,
|
|
callback: Optional[AccountManagerCallback] = None,
|
|
handler: Any = None) -> AccountManagerFuture:
|
|
future = AccountManagerFuture()
|
|
future._set_result(None)
|
|
if callback:
|
|
callback.run(future)
|
|
return future
|
|
|
|
# ── Listeners ──
|
|
|
|
def addOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener,
|
|
handler: Any = None,
|
|
updateImmediately: bool = False):
|
|
with self._lock:
|
|
self._listeners.append(listener)
|
|
self._listener_handler[listener] = handler
|
|
|
|
def removeOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener):
|
|
with self._lock:
|
|
if listener in self._listeners:
|
|
self._listeners.remove(listener)
|
|
self._listener_handler.pop(listener, None)
|
|
|
|
def _notify_listeners(self):
|
|
with self._lock:
|
|
accounts = list(self._accounts.values())
|
|
for listener in self._listeners:
|
|
listener.onAccountsUpdated(accounts)
|
|
|
|
def toString(self) -> str:
|
|
return f"AccountManager@{hex(id(self))}"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"Account",
|
|
"AccountManager",
|
|
"AccountManagerFuture",
|
|
"AccountManagerCallback",
|
|
"AuthenticatorDescription",
|
|
"OnAccountsUpdateListener",
|
|
] |