#!/usr/bin/python3

import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
import logging
import os
import signal
from datetime import datetime, timedelta
import time
import random
import configparser
from apscheduler.schedulers.background import BackgroundScheduler
from threading import Event 
from gettext import gettext as _
import gettext
import getpass
import subprocess
import re
import json
from typing import List, Dict
from dbus.types import Int32, Int64, String, Boolean, Double, Array,Dictionary
from dbus import service

UNATTENDED_UPGRADE_TIMESTAMP = "/var/lib/unattended-upgrades/unattended-upgrades-timestamp"
UNATTENDED_UPGRADE_POLICY_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrades-policy.conf"
UPDATE_ID_CONF_FILE_PATH="/var/lib/kylin-software-properties/config/updateID.conf"
ACTION_INSTALL = 1
ACTION_CHECK_RESOLVER = 3
ACTION_DOWNLOADONLY = 4
        
def signal_term_handler(signal,frame):
    # type: (int, object) -> None
    logging.warning("SIGTERM received, will stop")
    os._exit(1)

def ReadValueFromFile(file,section,option):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    try:
        config.read(file)
        value = config[section][option]
        return value
    except Exception as e:
        logging.error(_("read config file error:%s")%e)
        return None

def WriteValueToFile(file,section,option,value):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    config.add_section(section)
    config.set(section,option,value)
    config.write(open(file,"w"))   

def UpdateTimeStamp(task_id,last_run_time):
    logging.debug(_("update timestamp:%s %s")%(task_id,last_run_time))
    if os.path.exists(UNATTENDED_UPGRADE_TIMESTAMP):
        config=configparser.ConfigParser(allow_no_value=True)
        config.optionxform = str
        config.read(UNATTENDED_UPGRADE_TIMESTAMP)
        if 'TimeStamp' in config.sections():
            config.set('TimeStamp',task_id,last_run_time)  
            with open(UNATTENDED_UPGRADE_TIMESTAMP,'w') as f:
                config.write(f)


def ini_to_json(ini_path: str) -> dict:
    """
    将INI配置文件转换为JSON格式的字典对象
    
    Args:
        ini_path: INI文件路径
        
    Returns:
        dict: 转换后的JSON格式字典
        
    Raises:
        FileNotFoundError: 当INI文件不存在时抛出
        ValueError: 当文件格式错误时抛出
    """
    # 初始化配置解析器
    config = configparser.ConfigParser()
    
    # 读取INI文件
    if not config.read(ini_path):
        raise FileNotFoundError(f"文件 {ini_path} 未找到")
    
    # 构建嵌套字典
    result = {}
    for section in config.sections():
        result[section] = dict(config.items(section))  # 转换section为字典结构
        
    return result

def json_to_asv(data):
    """
    将 JSON 字典转换为 D-Bus a{sv} 类型
    参数：
        data: dict - 输入字典（需确保键为字符串）
    返回：
        dbus.Dictionary - 符合 a{sv} 签名的 D-Bus 字典
    异常：
        ValueError - 输入类型错误或数据格式不合法
        TypeError - 存在不支持的类型
    """
    def _convert_value(value):
        # 递归处理嵌套结构
        if isinstance(value, dict):
            return Dictionary(
                {k: _convert_value(v) for k, v in value.items()},
                signature=dbus.Signature('sv'),
                variant_level=1  # 隐式创建变体层
            )
        elif isinstance(value, list):
            return Array(
                [_convert_value(v) for v in value],
                signature=dbus.Signature('v'),
                variant_level=1
            )
        elif isinstance(value, bool):
            return Boolean(value)
        elif isinstance(value, int):
            return Int64(value) if abs(value) > 0x7FFFFFFF else Int32(value)
        elif isinstance(value, float):
            return Double(value)
        elif isinstance(value, str):
            return String(value)
        else:
            raise TypeError(f"不支持的类型: {type(value)}")

    try:
        # 输入类型验证
        if not isinstance(data, dict):
            raise ValueError("输入必须是字典类型")
        
        # 键类型验证（需全部为字符串）
        if not all(isinstance(k, str) for k in data.keys()):
            raise ValueError("字典键必须是字符串")
        
        return Dictionary(
            {k: _convert_value(v) for k, v in data.items()},
            signature=dbus.Signature('sv'),
            variant_level=1
        )
    except (TypeError, ValueError) as e:
        raise e
    except Exception as e:
        raise RuntimeError(f"未知错误: {str(e)}") from e
    
def process_time_intervals(data: List[Dict], max_random_minutes: int) -> List[Dict]:
    """
    合并重叠时间区间并生成随机调整后的时间段
    :param data: 输入时间段列表 [{"start":"HH:MM","end":"HH:MM"}]
    :param max_random_minutes: 最大随机偏移分钟数
    :return: 合并调整后的时间段列表
    """
    # 时间转换函数（支持跨天时间）
    def time_to_minutes(t: str) -> int:
        h, m = map(int, t.split(':'))
        return h * 60 + m

    # 分钟数转时间（支持跨天显示）
    def minutes_to_time(m: int) -> str:
        total_m = m % 1440
        return f"{total_m//60:02d}:{total_m%60:02d}"

    # 区间合并算法
    intervals = []
    for period in data:
        start = time_to_minutes(period["start"])
        end = time_to_minutes(period["end"])
        if end < start:  # 处理跨天情况
            intervals.append((start, end + 1440))
        else:
            intervals.append((start, end))

    # 排序并合并
    intervals.sort(key=lambda x: x[0])
    merged = []
    for current in intervals:
        if not merged:
            merged.append(list(current))
        else:
            last = merged[-1]
            if current[0] <= last[1]:
                last[1] = max(last[1], current[1])
            else:
                merged.append(list(current))

    # 生成随机调整后的时间段
    result = []
    for start, end in merged:
        # 计算有效时间跨度（考虑跨天）
        total_minutes = end - start
        random_offset = random.randint(0, min(max_random_minutes, total_minutes))

        # 生成新开始时间
        new_start = (start + random_offset) % 1440

        # 处理跨天显示逻辑
        original_end = end % 1440 if end > 1440 else end
        result.append({
            "start": minutes_to_time(new_start),
            "end": minutes_to_time(original_end)
        })

    return result

def process_timelist(timelist: List[Dict]) -> List[Dict]:
    """处理跨天时间段并生成标准化输出

    Args:
        timelist: 时间区间列表，示例 [{"start":"23:00","end":"11:00"},...]

    Returns:
        包含小时、分钟和超时时间的字典列表，示例 [{"hour":23,"minute":0,"timeout":720}]
    """
    def time_to_minutes(t: str) -> int:
        """将HH:MM转换为分钟数"""
        hours, minutes = map(int, t.split(':'))
        return hours * 60 + minutes

    result = []

    for period in timelist:
        # 解析起止时间
        start = time_to_minutes(period["start"])
        end = time_to_minutes(period["end"])

        # 处理跨天情况
        if end < start:
            end += 1440  # 24小时分钟数

        # 计算超时分钟数
        timeout = end - start

        # 提取小时和分钟
        start_h, start_m = divmod(start, 60)

        result.append({
            "hour": start_h,
            "minute": start_m,
            "timeout": timeout
        })

    return result

def convert_a_sv_to_json(a_sv_data):
    """
    将DBus的a{sv}类型数据转换为JSON对象
    
    Args:
        a_sv_data (dbus.Dictionary): D-Bus类型的a{sv}数据（字典格式）
    
    Returns:
        str: JSON格式字符串
    """
    def _parse_variant(value):
        # 递归解析变体类型中的嵌套数据
        if isinstance(value, dbus.Dictionary):
            return {k: _parse_variant(v) for k, v in value.items()}
        elif isinstance(value, dbus.Array):
            return [_parse_variant(v) for v in value]
        elif isinstance(value, dbus.Struct):
            return tuple(_parse_variant(v) for v in value)
        elif isinstance(value, (dbus.String, dbus.ObjectPath)):
            return str(value)
        elif isinstance(value, dbus.Boolean):
            return bool(value)
        elif isinstance(value, (dbus.Int16, dbus.Int32, dbus.Int64)):
            return int(value)
        elif isinstance(value, (dbus.Double, dbus.UInt16, dbus.UInt32, dbus.UInt64)):
            return float(value) if '.' in str(value) else int(value)
        else:
            return value

    # 转换主逻辑
    json_dict = {}
    for key, variant in a_sv_data.items():
        json_dict[key] = _parse_variant(variant)
    
    return json.dumps(json_dict, indent=2)

def parse_time(time_str: str) -> dict:
    """将HH:MM格式的时间字符串解析为字典
    
    Args:
        time_str: 时间字符串，支持中文/英文冒号，如"20:00"或"20：00"
        
    Returns:
        包含小时和分钟的字典，示例: {'hour':20, 'minute':0}
        
    Raises:
        ValueError: 当格式无效或数值越界时抛出
    """
    # 预处理字符串
    normalized_str = time_str.replace('：', ':')  # 中文冒号转英文
    parts = normalized_str.split(':')
    
    # 格式验证
    if len(parts) != 2:
        raise ValueError(f"无效时间格式: {time_str}，应为HH:MM")
    
    try:
        hour = int(parts[0])
        minute = int(parts[1])
    except ValueError:
        raise ValueError(f"非数字时间值: {time_str}")
    
    # 数值范围验证
    if not (0 <= hour <= 23):
        raise ValueError(f"小时值越界: {hour}，应为0-23")
    if not (0 <= minute <= 59):
        raise ValueError(f"分钟值越界: {minute}，应为0-59")
    
    return {'hour': hour, 'minute': minute}

def parse_time_range(time_str: str) -> dict:
    """
    解析时间字符串并生成时间范围字典
    
    参数：
    time_str - 输入时间字符串，支持三种格式：
               "HH:MM"、"HH:MM:SS"、"HH:MM-HH:MM"
    
    返回：
    {'start': 'HH:MM', 'end': 'HH:MM'}
    
    异常：
    ValueError - 当输入格式无效或时间值越界时抛出
    """
    # 格式验证正则表达式
    time_pattern = r'^([0-2][0-9]:[0-5][0-9])(?::[0-5][0-9])?(?:-([0-2][0-9]:[0-5][0-9]))?$'
    
    if match := re.match(time_pattern, time_str):
        start_time, end_time = match.groups()
        
        # 处理第三种格式（时间区间）
        if end_time:
            return validate_and_format(start_time, end_time)
        
        # 处理前两种格式（单个时间）
        base_time = time_str[:5]  # 截取HH:MM部分
        calculated_end = calculate_end_time(base_time)
        return {'start': base_time, 'end': calculated_end}
    
    raise ValueError(f"无效时间格式：{time_str}")

def validate_and_format(start: str, end: str) -> dict:
    """验证并格式化时间区间"""
    try:
        # 时间有效性验证
        datetime.strptime(start, '%H:%M')
        datetime.strptime(end, '%H:%M')
        return {'start': start, 'end': end}
    except ValueError as e:
        raise ValueError(f"无效时间值：{str(e)}")

def calculate_end_time(base: str) -> str:
    """计算3小时后时间（处理跨天）"""
    try:
        # 时间解析与计算
        base_dt = datetime.strptime(base, '%H:%M')
        end_dt = base_dt + timedelta(hours=12)
        return end_dt.strftime('%H:%M')
    except ValueError as e:
        raise ValueError(f"无效基准时间：{str(e)}")


def check_startup_download_time(strategy_json):
    """
    检查启动下载时间是否在允许的时间段内
    
    参数:
    strategy_json: kylin-update-strategy.json 的 JSON 对象
    
    返回:
    如果启用启动下载且当前时间加上等待分钟数在允许的下载时间内，则返回一个元组 (future_time, timeout_in_seconds)，其中timeout_in_seconds是future_time到时间段结束时间的秒数；
    如果存在多个符合条件的时间段，选择结束时间最晚的进行计算；
    否则返回 None
    """
    # 获取 autoupdate 配置
    autoupdate_config = strategy_json.get('autoupdate', {})
    
    # 检查 startup_download 是否启用
    startup_download = autoupdate_config.get('startup_download', {})
    enable = startup_download.get('enable', False)
    
    if not enable:
        return None
    
    # 获取等待分钟数
    wait_minutes = startup_download.get('wait_minutes', 0)
    
    # 计算当前时间加上等待分钟数后的时间
    future_time = datetime.now() + timedelta(minutes=wait_minutes)
    
    # 获取下载时间段
    download_times = autoupdate_config.get('downloadtime', [])
    
    # 找到所有符合条件的时间段
    valid_periods = []
    for period in download_times:
        start_str = period.get('start')
        end_str = period.get('end')
        
        if start_str and end_str:
            start_time = datetime.strptime(start_str, '%H:%M').time()
            end_time = datetime.strptime(end_str, '%H:%M').time()
            
            # 创建日期时间对象用于比较
            future_time_only = future_time.time()
            
            # 处理跨天的情况（例如从晚上到第二天早上的时间段）
            if end_time < start_time:
                # 跨天情况：时间范围跨越午夜
                if future_time_only >= start_time or future_time_only <= end_time:
                    # 为了正确计算跨天时间段的结束时间，我们需要考虑日期
                    # 如果当前时间大于等于开始时间，说明在当天的后半段
                    # 如果当前时间小于结束时间，说明在下一天的凌晨
                    if future_time_only >= start_time:
                        # 当前时间在开始时间之后（但仍在跨天时间段内）
                        # 结束时间应该是第二天的结束时间
                        end_datetime = datetime.combine(future_time.date() + timedelta(days=1), end_time)
                    else:
                        # 当前时间在开始时间之前（但在跨天时间段内，即接近第二天的结束时间）
                        # 结束时间应该是今天的结束时间
                        end_datetime = datetime.combine(future_time.date(), end_time)
                        if end_datetime.time() > future_time_only:
                            # 如果今天结束时间比当前时间晚，那就是正确的
                            pass
                        else:
                            # 否则，应该是明天的结束时间
                            end_datetime = datetime.combine(future_time.date() + timedelta(days=1), end_time)
                    
                    valid_periods.append((end_datetime, start_time, end_time))
            else:
                # 不跨天的情况
                if start_time <= future_time_only <= end_time:
                    # 同一天的结束时间
                    end_datetime = datetime.combine(future_time.date(), end_time)
                    valid_periods.append((end_datetime, start_time, end_time))
    
    # 如果没有符合条件的时间段，返回 None
    if not valid_periods:
        return None
    
    # 找到结束时间最晚的时间段
    latest_end_period = max(valid_periods, key=lambda x: x[0])
    latest_end_datetime = latest_end_period[0]
    
    # 计算 future_time 到时间段结束时间的秒数
    timeout_in_seconds = int((latest_end_datetime - future_time).total_seconds())
    
    # 确保超时时间不为负数
    if timeout_in_seconds < 0:
        # 如果是跨天情况，可能需要加一天
        if latest_end_datetime.date() > future_time.date():
            # 跨天情况下重新计算
            next_day_end = datetime.combine(future_time.date() + timedelta(days=1), latest_end_period[2])
            timeout_in_seconds = int((next_day_end - future_time).total_seconds())
        else:
            # 如果仍然为负，说明时间已经过了该时间段
            return None
    
    return (future_time, timeout_in_seconds)

class NotifyService(service.Object):
    def __init__(self):
        # 连接到会话总线并注册对象路径
        bus = dbus.SystemBus()
        service.Object.__init__(self,bus, '/com/kylin/notifysend')

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='is')  # 参数类型定义为int
    def RebootNotify(self, status_code,status_description):
        """自定义信号声明：参数为整数状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='is')  # 参数类型定义为int
    def InstallFinishNotify(self, status_code,status_description):
        """自定义信号声明：参数为整数状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

    @dbus.service.signal(dbus_interface='com.kylin.notifysend', 
                        signature='b')  # 参数类型定义为bool
    def ConnectDistUpgrade(self, status):
        """自定义信号声明：参数为是否连接备份和下载完成信号状态码"""
        pass  # 函数体为空，通过调用该方法触发信号

class UnattendedUpgradesShutdown():
    def __init__(self) -> None:
        DBusGMainLoop(set_as_default=True)
        self.loop = GLib.MainLoop()
        self.system_bus = dbus.SystemBus()
        self.update_utils_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade/utils')
        self.update_utils_interface = dbus.Interface(self.update_utils_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        self.update_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade',follow_name_owner_changes=True)
        self.update_interface = dbus.Interface(self.update_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        self.upgrade_strategy_proxy = self.system_bus.get_object('com.kylin.UpgradeStrategies','/com/kylin/UpgradeStrategies',follow_name_owner_changes=True)
        self.upgrade_strategy_interface = dbus.Interface(self.upgrade_strategy_proxy,dbus_interface='com.kylin.UpgradeStrategies.interface')
        self.backup_proxy = self.system_bus.get_object('com.kylin.backupserver','/',follow_name_owner_changes=True)
        self.backup_interface = dbus.Interface(self.backup_proxy,dbus_interface='com.kylin.backup.server')
        self.update_proxy.connect_to_signal('UpdateDetectFinished',self.update_detect_finished_handler)                                        
        self.update_proxy.connect_to_signal('UpdateFixBrokenStatus',self.update_fix_broken_status)
        self.update_proxy.connect_to_signal('UpdateDependResloveStatus',self.update_depend_resolve_status)                                           
        self.update_proxy.connect_to_signal('UpdateDloadAndInstStaChanged',self.update_download_install_status)
        self.update_proxy.connect_to_signal('UpdateInstallFinished',self.update_install_finished)                                        
        self.update_proxy.connect_to_signal('UpdateDownloadFinished',self.update_download_finished)
        self.update_proxy.connect_to_signal('InstallDetectStatus',self.install_detect_status_handler)
        # self.update_proxy.connect_to_signal("ChangeUpgradePolicy",self.change_upgrade_policy_handler)   
        # self.update_proxy.connect_to_signal("UpgradeAllNow",self.upgrade_all_now_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("AutocheckStatusChanged",self.auto_check_status_changed_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("AutoUpgradeStatusChanged",self.autoupgrade_status_changed_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("AutoupgradePeriodChanged",self.autoupgrade_period_changed_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("AutoupgradeDetailChanged",self.autoupgrade_detail_changed_handler)
        self.upgrade_strategy_proxy.connect_to_signal("UpgradeSchemeChanged",self.change_upgrade_scheme_handler)
        self.upgrade_strategy_proxy.connect_to_signal("UpgradeStrategyChanged",self.change_upgrade_strategy_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("PropertyChanged",self.property_changed_handler)
        # self.upgrade_strategy_proxy.connect_to_signal("UpgradeAllNow",self.upgrade_all_now_handler)   
        self.backup_proxy.connect_to_signal('sendBackupResult',self.backup_result_handler)                                          
        self.backup_proxy.connect_to_signal('sendRate',self.send_rate_handler) 
        self.update_detect_status = False
        self.update_detect_event = Event()
        self.update_list = []
        self.resolve_depend_status = False
        self.resolve_depend_status_event = Event()
        self.remove_pkgs = []
        self.install_start_status = False
        self.install_finish_status = False
        self.install_finish_status_event = Event()
        self.install_finish_group = []
        self.download_start_status = False
        self.download_finish_status = False
        self.download_finish_status_event = Event()
        self.download_finish_group = []
        self.install_detect_status=False
        self.install_detect_event=Event()
        self.backup_finish_result = False
        self.backup_finish_event = Event()    
        self.need_check_backup=True
        self.need_check_uuid=True
        self.need_check_updatelist=True
        self.notify_service = NotifyService()
        self.shutdown_install = 'True'
        self.error_code=""
        self.uuid=""
        self.backup_finish_status = 32

    def DataBackendCollect(self,updateinfo,json_file):
        self.update_utils_interface.DataBackendCollect(updateinfo,json_file)

    def GetConfigValue(self,section,option):
        ret1,ret2=self.update_interface.GetConfigValue(section,option)
        return ret2
    
    def GetAutoBackupStatus(self):
        return self.upgrade_strategy_interface.GetAutoBackupStatus()
    
    def SetConfigValue(self,section,option,value):
        return self.update_interface.SetConfigValue(section,option,value)
    
    def GetUpgradeStrategy(self):
        return self.upgrade_strategy_interface.GetUpgradeStrategy()
    
    def SetUpgradeStrategy(self,input):
        return self.upgrade_strategy_interface.SetUpgradeStrategy(input)
    
    def CancelDownload(self):
        return self.update_interface.CancelDownload()
    
    def GetBackendStatus(self):
        return self.update_interface.GetBackendStatus("")

    def init_dbus_connections(self):
        pass

    def init_config(self):
        pass

    def init_event_flags(self):
        pass

    def init_scheduler(self):
        pass

    def run(self):
        pass

    def prepare_upgrade(self):
        logging.info(_("prepare for upgrade"))
        self.update_detect_event.clear()
        self.update_interface.UpdateDetect()
        self.update_detect_event.wait()
        logging.debug(_("update detect finish:%s,%s")%(self.update_detect_status,",".join(self.update_list)))
        if self.update_detect_status and len(self.update_list)>0:
            pass
        elif not self.update_detect_status and 'kylin-system-updater' in self.update_list:
            logging.info(_("self update finished"))
        else:
            return False
        if(self.need_check_uuid):
            uuid=ReadValueFromFile(UPDATE_ID_CONF_FILE_PATH,"update","uuid")
            logging.info("new uuid:%s,old uuid:%s"%(uuid,self.uuid))
            if(self.uuid==uuid):
                pass
            else:
                return False
        if(self.need_check_updatelist):
            download_list=",".join(self.download_finish_group)
            update_detect_list=",".join(self.update_list)
            logging.info("old update list:%s,new update list:%s"%(download_list,update_detect_list))
            if(self.download_finish_group==self.update_list):
                pass
            else:
                return False
        self.resolve_depend_status_event.clear()
        self.update_interface.DistUpgradeForAuto(ACTION_CHECK_RESOLVER)
        self.resolve_depend_status_event.wait()
        logging.debug(_("resolve dependency status:%s,%s")%(self.resolve_depend_status,",".join(self.remove_pkgs)))
        if self.resolve_depend_status and len(self.remove_pkgs)==0:
            pass
        else:
            return False
        logging.info("disconnect notifysend from downloadfinish and backup result")
        self.notify_service.ConnectDistUpgrade(False)
        needbackup=self.upgrade_strategy_interface.GetAutoBackupStatus()
        if(self.need_check_backup):
            logging.debug(_("checking if need backup:%s")%needbackup)
            if needbackup:
                if self.backup():
                    logging.debug(_("backup success"))
                    ret = self.SetConfigValue("UpdateFrontendConf","backup_exist","True")
                    logging.info(_("set config value :%s")%(ret))
                    restorable_flag="/tmp/update-backup.success"
                    logging.info("create restorable flag:%s"%(restorable_flag))
                    with open(restorable_flag, 'w') as file:
                        file.write("restore")
                else:
                    logging.debug(_("backup failed"))  
                    BackupInfo={}                                                      
                    BackupInfo.update({"errorCode":str(self.backup_finish_status),
                                    "error_string":"备份失败",
                                    "appname":",".join(self.update_list)})
                    BackupInfo.update({"status":"failed"})
                    json_file = json.dumps(BackupInfo.copy())
                    self.DataBackendCollect("BackupInfo",json_file)
                    return False
        return True

    def download(self,timeout):
        logging.debug(_("start download"))
        logging.info("download timeout:%d"%timeout)
        self.uuid=ReadValueFromFile(UPDATE_ID_CONF_FILE_PATH,"update","uuid")
        logging.info("uuid:%s"%self.uuid)
        self.download_start_status=True
        self.download_finish_status=False
        self.download_finish_group=[]
        self.download_finish_status_event.clear()
        errcode,errstr = self.update_interface.DistUpgradeForAuto(ACTION_DOWNLOADONLY)
        logging.info("ret code:%d,ret string:%s"%(errcode,errstr))
        if(errcode!=0):
            return False
        self.download_finish_status_event.wait(timeout=timeout)
        backend_status=self.GetBackendStatus()
        logging.info("backend status:%d"%backend_status)
        if(backend_status==ACTION_DOWNLOADONLY):
            logging.info("cancel download")
            self.CancelDownload()
            time.sleep(1)
        logging.debug(_("download finish status:%s,%s")%(self.download_finish_status,",".join(self.download_finish_group)))
        if self.download_finish_status and len(self.download_finish_group)>0:
            pass
        else:
            return False
        return True 
    
    def install_basic(self):
        logging.debug(_("start install"))   
        self.install_start_status=True
        self.install_finish_status_event.clear()
        self.update_interface.DistUpgradeForAuto(ACTION_INSTALL)
        self.install_finish_status_event.wait()        
        logging.debug(_("install finish status:%s,%s")%(self.install_finish_status,",".join(self.install_finish_group)))
        if self.install_finish_status and len(self.install_finish_group)>0:
            pass
        else:
            return False
        return True      

    def install(self):
        pass
    
    def download_with_timeout(self,timeout):
        pass
         
    def predownload_with_timeout(self,timeout):
        pass

    def cancel(self):
        pass

    def backup(self):
        logging.info(_("start backup"))
        self.backup_proxy = self.system_bus.get_object('com.kylin.backupserver','/',follow_name_owner_changes=True)
        self.backup_interface = dbus.Interface(self.backup_proxy,dbus_interface='com.kylin.backup.server')
        self.backup_proxy.connect_to_signal('sendBackupResult',self.backup_result_handler)                                          
        self.backup_proxy.connect_to_signal('sendRate',self.send_rate_handler) 
        backup_name = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        userName=getpass.getuser()
        uid=os.getuid()
        self.backup_finish_event.clear()
        self.backup_interface.autoBackUpForSystemUpdate_noreturn(backup_name,userName,uid)
        self.backup_finish_event.wait()
        return self.backup_finish_result
    
    def reboot(self):
        subprocess.run(["systemctl", "reboot"], check=True)

    def auto_check_status_changed_handler(self,status):
        pass

    def change_upgrade_strategy_handler(self,diff):
        pass
    
    def change_upgrade_scheme_handler(self,scheme):
        pass
        
    def change_upgrade_policy_handler(self):
        pass

    def autoupgrade_status_changed_handler(self,autoupgradestate):
        pass

    def autoupgrade_detail_changed_handler(self,mask,time):
        pass

    def autoupgrade_period_changed_handler(self,day):
        pass

    def property_changed_handler(self,property, value): 
        pass
        
    def upgrade_all_now_handler(self):
        pass

    def update_detect_finished_handler(self,success,updatelist,error_status,error_cause):
        logging.info(_("update detect finished:sucess:%s,updatelist:%s,error_status:%s,error_cause:%s")\
            %(success,",".join(updatelist),error_status,error_cause))
        self.update_detect_status = success
        self.update_list = updatelist
        self.update_detect_event.set()

        
    def update_fix_broken_status(self,resolver_status,remove_status,remove_pkgs,pkg_raw_description,delete_desc,error_string,error_desc):
        logging.info(_("update fix broken status:resolver_status:%s,remove_status:%s,error_string:%s,error_desc:%s")%(resolver_status,remove_status,error_string,error_desc))
        self.update_detect_status = False
        self.update_list = []
        self.update_detect_event.set()    
        
    def update_depend_resolve_status(self,resolver_status,remove_status,remove_pkgs,pkg_raw_description,delete_description,error_string,error_desc):
        logging.info(_("update depend resove status:%s,remove status:%s,remove pkgs:%s,pkg raw description:%s,delete_descrition:%s,error string:%s,error desc:%s")\
            %(resolver_status,remove_status,",".join(remove_pkgs),",".join(pkg_raw_description),",".join(delete_description),error_string,error_desc))
        self.resolve_depend_status = resolver_status
        self.remove_pkgs = remove_pkgs
        self.resolve_depend_status_event.set()
        self.shutdown_install=self.GetConfigValue("InstatllMode","shutdown_install")
        
    def update_download_install_status(self,group,progress,status,details):
        logging.debug(_("%s update progress:%d,status:%s,details:%s")%(",".join(group),progress,status,details))
        
    def update_install_finished(self,success,group,error_string,error_desc):
        logging.info(_("update install finish success:%s,group:%s,error string:%s,error desc:%s")\
            %(success,",".join(group),error_string,error_desc))           
        if(self.download_start_status):
            logging.info("this is a download job")
            self.download_finish_status = success
            self.download_finish_group = group
            self.download_finish_status_event.set()
            if(success):
                self.download_start_status=False
        if(self.install_start_status):
            logging.info("this is an install job")
            self.install_finish_status = success
            self.install_finish_group = group
            self.install_finish_status_event.set()
            if(success):
                self.install_start_status=False
                self.download_start_status=False
                self.download_finish_status=False
        logging.info("shutdown install:%s"%(self.shutdown_install))
        if(self.shutdown_install=='True'):
            logging.info("received a install finish signal")
            '''
            errcodelist=["#0208","#0204"]
            if(error_string in errcodelist):
                logging.info("error string is %s,do not clear the delay count and button hide status")
            else:
                logging.info("reset hideupdate and delaycount")
                strategy_data={
                    "autoupdate":{
                        "hideupdate":False,
                        "delaycount":0                   
                        }
                    } 
                self.SetUpgradeStrategy(json_to_asv(strategy_data))
            '''
        elif(self.shutdown_install=='False'):
            logging.info("received a download finish signal")
        else:
            logging.info("unknown install mode:%s"%(self.shutdown_install))

    def update_download_finished(self,success,group,error_string,error_desc):
        logging.info(_("update download finisih success:%s,group:%s,error string:%s,error desc:%s")\
            %(success,",".join(group),error_string,error_desc))
        self.download_finish_status = success    
        self.download_finish_group = group
        self.download_finish_status_event.set()
        if(success):
            self.download_start_status=False
                        
    def backup_result_handler(self,result,status):
        logging.debug(_("backup result:%s,status:%d")%(result,status))                  
        self.backup_finish_result = result 
        self.backup_finish_status = status
        self.backup_finish_event.set()
        
    def send_rate_handler(self,sta,pro):
        logging.debug(_("backup status:%d,progress:%d")%(sta,pro))    

    def install_detect_status_handler(self,success,error_code):
        logging.info("install detect status:%s,%s"%(success,error_code))
        self.error_code=error_code
        self.install_detect_status=success
        self.install_detect_event.set()
    
class UnattendedUpgradeBackend(UnattendedUpgradesShutdown):
    def __init__(self):
        super().__init__()

    def init_config(self):
        updagradestrategy=self.GetUpgradeStrategy()
        configjsonstr=convert_a_sv_to_json(updagradestrategy)
        logging.info(configjsonstr)
        self.config={}
        self.config=json.loads(configjsonstr)
    
    def init_scheduler(self):
        self.background_scheduler=BackgroundScheduler()
        self.background_scheduler.start()
        self.schedule_task()
        startup_downloadtime=check_startup_download_time(self.config)
        logging.info("startup downloadtime:")
        logging.info(startup_downloadtime)
        if(startup_downloadtime is None):
            pass
        else:
            future_time=startup_downloadtime[0]
            timeout_in_seconds=startup_downloadtime[1]
            task_id="startup download"
            self.background_scheduler.add_job(self.download_with_timeout,args=[timeout_in_seconds],trigger='date',run_date=future_time,id=task_id,replace_existing=True)

        with open("/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log",'a+') as f:
            self.background_scheduler.print_jobs(out=f)  

    def remove_non_startup_download_jobs(self):
        """
        移除 background_scheduler 中所有名字不为 'startup download' 的任务
        
        只有当 self.config 中的 autoupdate.startup_download.enable 为 true 时，
        才保留 task_id 为 'startup download' 的任务，否则移除所有任务。
        """
        if not hasattr(self, 'background_scheduler'):
            logging.warning("background_scheduler 不存在，无法移除任务")
            return
        
        try:
            # 检查 startup_download 配置是否启用
            enable_startup_download = False
            if 'autoupdate' in self.config:
                autoupdate_config = self.config.get('autoupdate', {})
                startup_download_config = autoupdate_config.get('startup_download', {})
                enable_startup_download = startup_download_config.get('enable', False)
            
            logging.info("startup_download enable: %s", enable_startup_download)
            
            # 获取所有任务
            jobs = self.background_scheduler.get_jobs()
            
            # 根据配置决定是否保留 'startup download' 任务
            for job in jobs:
                should_keep = False
                
                # 只有当配置启用且任务 ID 为 'startup download' 时才保留
                if enable_startup_download and job.id == "startup download":
                    should_keep = True
                    logging.info("保留任务：%s (startup_download 已启用)", job.id)
                
                if not should_keep:
                    self.background_scheduler.remove_job(job.id)
                    logging.info("已移除任务：%s", job.id)
            
            # 记录剩余的任务
            remaining_jobs = self.background_scheduler.get_jobs()
            logging.info("当前剩余任务数：%d", len(remaining_jobs))
            for job in remaining_jobs:
                logging.info("  - %s (trigger: %s, next_run: %s)", 
                            job.id, job.trigger, job.next_run_time)
        
        except Exception as e:
            logging.error("移除任务时出错：%s", e)


    def schedule_task(self):
        try:
            updatemode=self.config.get('updatemode','off')
            logging.info("upgrade mode:%s"%updatemode)
            if(updatemode=="off"):
                pass
            elif(updatemode=="autoupdate"):
                autoupdateconfig=self.config.get("autoupdate",{})
                downloadtime=autoupdateconfig.get("downloadtime",[])
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                period=autoupdateconfig.get("period",1)
                mode=autoupdateconfig.get("mode",1)
                timelist=process_timelist(process_time_intervals(downloadtime,downloadrandom))
                logging.info(timelist)
                downloadtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','download')
                if not downloadtimestamp:
                    now = datetime.now() 
                    downloadtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                dtime=datetime.strptime(downloadtimestamp, "%Y-%m-%d %H:%M:%S")
                for t in timelist:
                    taskdate=(dtime+timedelta(days=float(period))).replace(hour=t.get("hour",0),minute=t.get("minute",0))
                    task_id="download_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.download_with_timeout,args=[t.get("timeout",30)*60],trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
                if(mode=="timing"):
                    installtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','install')
                    if not installtimestamp:
                        now = datetime.now() 
                        installtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                    itime=datetime.strptime(installtimestamp, "%Y-%m-%d %H:%M:%S")
                    installtime=autoupdateconfig.get("installtime","20:00")
                    installtimedict=parse_time(installtime)
                    logging.info(installtimedict)
                    taskdate=(itime+timedelta(days=float(period))).replace(hour=installtimedict.get("hour",0),minute=installtimedict.get("minute",0))
                    task_id="install_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.install,trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
            elif(updatemode=="predownload"):
                autoupdateconfig=self.config.get("predownload",{})
                downloadtime=autoupdateconfig.get("downloadtime",[])
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                period=autoupdateconfig.get("period",1)
                timelist=process_timelist(process_time_intervals(downloadtime,downloadrandom))
                logging.info(timelist)
                downloadtimestamp=ReadValueFromFile(UNATTENDED_UPGRADE_TIMESTAMP,'TimeStamp','predownload')
                if not downloadtimestamp:
                    now = datetime.now() 
                    downloadtimestamp = now.strftime("%Y-%m-%d %H:%M:%S")
                dtime=datetime.strptime(downloadtimestamp, "%Y-%m-%d %H:%M:%S")
                for t in timelist:
                    taskdate=(dtime+timedelta(days=float(period))).replace(hour=t.get("hour",0),minute=t.get("minute",0))
                    task_id="predownload_task_%s"%(taskdate.strftime("%Y-%m-%d %H:%M:%S"))
                    self.background_scheduler.add_job(self.predownload_with_timeout,args=[t.get("timeout",30)*60],trigger='interval',days=period,start_date=taskdate,id=task_id,replace_existing=True)
            elif(updatemode=="updatenow"):            
                autoupdateconfig=self.config.get("updatenow",{})
                downloadrandom=autoupdateconfig.get("downloadtimerandom",120)
                delay = random.uniform(0, downloadrandom)
                delta = timedelta(minutes=delay)
                taskdate=datetime.now() + delta
                self.background_scheduler.add_job(self.install,'date',run_date=taskdate)
            else:
                pass 
        except Exception as e:
            logging.error(e) 

    def install(self):
        pass
        '''
        check_download_before_install=self.config.get("autoupdate",{}).get("check_download_before_install",False)
        logging.info("start install,check if need finish download:%s"%check_download_before_install)
        if(check_download_before_install):
            logging.info("there's running download job:%s,last download finish status:%s"%(self.download_start_status,self.download_finish_status))
            if(self.download_start_status and not self.download_finish_status):
                logging.info("last download unfinished,quit install and report")
                InstallInfo={}                                                      
                InstallInfo.update({"errorCode":str(self.download_finish_status),
                                "error_string":"下载未完成，无法进行安装",
                                "appname":",".join(self.update_list)})
                InstallInfo.update({"status":"failed"})
                json_file = json.dumps(InstallInfo.copy())
                self.DataBackendCollect("Downloaded",json_file)
                return
            elif(not self.download_start_status and self.download_finish_status):
                logging.info("download finished,ready to install")
                uuid=ReadValueFromFile(UPDATE_ID_CONF_FILE_PATH,"update","uuid")
                logging.info("new uuid:%s,old uuid:%s"%(uuid,self.uuid))
                if(self.uuid!=uuid):
                    logging.info("uuid changed,no need to install")
                    return
            else:
                logging.info("no need to install anything")
                return
        self.need_check_backup=True
        if(self.prepare_upgrade()):
            if(self.install_basic()):
                if(check_download_before_install):
                    logging.info("reset update download finish status false")
                    self.download_finish_status=False
                updatemode=self.config.get("updatemode","autoupdate")
                logging.info("updatemode:%s"%updatemode)
                if(updatemode=="autoupdate"):
                    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    UpdateTimeStamp('install',now)
                    postaction=self.config.get("autoupdate",{}).get("postaction",'no')
                    logging.info("postaction:%s"%postaction)
                    if(postaction=="notify"):
                        self.notify_service.InstallFinishNotify(0,"install finished")
                    elif(postaction=="reboot"):
                        self.reboot()  
                elif(updatemode=="updatenow"):
                    postaction=self.config.get("autoupdate",{}).get("postaction",'no')
                    logging.info("postaction:%s"%postaction)
                    if(postaction=="notify"):
                        self.notify_service.InstallFinishNotify(0,"install finished")
                    elif(postaction=="reboot"):
                        self.reboot()
        '''  
    def install_after_download(self):
        self.need_check_backup=True
        self.need_check_uuid=True
        self.need_check_updatelist=True
        if(self.prepare_upgrade()):
            if(self.install_basic()):
                updatemode=self.config.get("updatemode","autoupdate")
                logging.info("updatemode:%s"%updatemode)
                if(updatemode=="autoupdate"):
                    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    UpdateTimeStamp('install',now)
                    postaction=self.config.get("autoupdate",{}).get("postaction",'no')
                    logging.info("postaction:%s"%postaction)
                    if(postaction=="notify"):
                        self.notify_service.InstallFinishNotify(0,"install finished")
                    elif(postaction=="reboot"):
                        self.reboot()  
                elif(updatemode=="updatenow"):
                    postaction=self.config.get("autoupdate",{}).get("postaction",'no')
                    logging.info("postaction:%s"%postaction)
                    if(postaction=="notify"):
                        self.notify_service.InstallFinishNotify(0,"install finished")
                    elif(postaction=="reboot"):
                        self.reboot()
                                
    def download_with_timeout(self,timeout):
        self.need_check_backup=False
        self.need_check_uuid=False
        self.need_check_updatelist=False
        wait_timeout=timeout
        start_time=time.time()
        prepare_upgrade_result=self.prepare_upgrade()
        download_result=False
        if(prepare_upgrade_result):
            end_time=time.time()
            time_diff=end_time-start_time
            if(wait_timeout>time_diff):
                wait_timeout=wait_timeout-time_diff
                logging.info("timeout:%d,time diff:%d"%(wait_timeout,time_diff))
            download_result=self.download(wait_timeout)
            if(download_result):
                install_after_download=self.config.get("autoupdate",{}).get("install_after_download",True)
                logging.info("need install after download:%s"%install_after_download)
                if(install_after_download):
                    self.install_after_download()
                now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                UpdateTimeStamp('download',now)
                mode=self.config.get("autoupdate",{}).get("mode","timing")
                logging.info("auto update install mode:%s"%mode)
                if(mode=="shutdown"):
                    imode=self.GetConfigValue("InstallMode","shutdown_install")
                    logging.info("install mode:%s"%imode)
                    if(imode=="True"):
                        logging.info("send reboot notify signal")
                        self.notify_service.RebootNotify(0,"download finish")
                        pass
        logging.info("prepare upgrade result:%s,download result:%s"%(prepare_upgrade_result,download_result))
        if(prepare_upgrade_result and download_result):
            pass
        else:
            download_retry_period=int(self.config.get("autoupdate",{}).get("download_retry_period",30))
            logging.info("download retry period:%d"%download_retry_period)
            if(download_retry_period>0):
                end_time=time.time()
                time_diff=end_time-start_time
                logging.info("wait timeout:%d,elapaed time:%d"%(wait_timeout,time_diff))
                if(wait_timeout>(time_diff+download_retry_period*60)):
                    wait_timeout=wait_timeout-(time_diff+download_retry_period*60)
                    logging.info("download retry timeout:%d,time diff:%d"%(wait_timeout,time_diff))
                    delta = timedelta(minutes=download_retry_period)
                    taskdate=datetime.now() + delta
                    self.background_scheduler.add_job(self.download_with_timeout,args=[wait_timeout],trigger='date',run_date=taskdate)
                with open("/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log",'a+') as f:
                    self.background_scheduler.print_jobs(out=f)  
                               
    def predownload_with_timeout(self,timeout):
        self.need_check_backup=False
        wait_timeout=timeout
        start_time=time.time()
        if(self.prepare_upgrade()):
            end_time=time.time()
            time_diff=end_time-start_time
            logging.info("timeout:%d,time diff:%d"%(wait_timeout,time_diff))
            if(wait_timeout>time_diff):
                wait_timeout=wait_timeout-time_diff
            if(self.download(wait_timeout)):
                now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                UpdateTimeStamp('predownload',now)

    def change_upgrade_strategy_handler(self, diff):  
        logging.info("upgrade strategy changed:%s"%diff)
        json_diff=json.loads(diff)
        updagradestrategy=self.GetUpgradeStrategy()
        configjsonstr=convert_a_sv_to_json(updagradestrategy)
        logging.info(configjsonstr)
        self.config=json.loads(configjsonstr)
        updatemode=self.config.get("updatemode","off")
        logging.info("upgrade mode:%s"%updatemode)
        added=json_diff.get("added",{})
        modified=json_diff.get("modified",{})
        if(not added and not modified):      
            logging.info("no changes of strategy")
        else:
            logging.info("strategy changed reschedule tasks")
            # self.background_scheduler.remove_all_jobs()
            self.remove_non_startup_download_jobs()
            self.schedule_task()
        with open("/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log",'a+') as f:
            self.background_scheduler.print_jobs(out=f)  

    def property_changed_handler(self, property, value):
        logging.info("property changed %s:%s"%(property,value))
        self.change_upgrade_policy_handler()

    def change_upgrade_policy_handler(self):
        logging.info("upgrade policy changed")
        json_data=ini_to_json(UNATTENDED_UPGRADE_POLICY_FILE_PATH)
        logging.info(json_data)
        data={}
        autoupgradepolicy=json_data.get('autoUpgradePolicy',{})
        updatedays=int(autoupgradepolicy.get('updatedays',1))
        randomrange=int(autoupgradepolicy.get('randomrange',1))
        if(autoupgradepolicy.get('autoupgradestate','off')=='on'):
            data.update({'updatemode':'autoupdate'})
            downloadmode=autoupgradepolicy.get('downloadmode','timing')
            downloadtime=parse_time_range(autoupgradepolicy.get('downloadtime','11:00-12:00'))
            installmode=autoupgradepolicy.get('installmode','timing')
            installtime=parse_time_range(autoupgradepolicy.get('installtime','10:00-11:00'))
            autoupdate={}
            if(downloadmode=='timing'):
                autoupdate.update({'downloadtime':[downloadtime]})
            if(installmode=='timing'):
                autoupdate.update({'mode':'timing'})
                autoupdate.update({'installtime':installtime.get('start',"10:00")})
            elif(installmode=='bshutdown'):
                autoupdate.update({'mode':'shutdown'})
            autoupdate.update({'downloadtimerandom': randomrange})
            autoupdate.update({'period':updatedays})
            data.update({'autoupdate':autoupdate})
        elif(autoupgradepolicy.get('predownload','off')=='on'):
            data.update({'updatemode':'predownload'})
            predownloadtime=parse_time_range(autoupgradepolicy.get('predownloadtime','10:00-11:00'))
            predownload={}
            predownload.update({"downloadtime":[predownloadtime]})
            predownload.update({'downloadtimerandom': randomrange})
            predownload.update({'period':updatedays})
            data.update({"predownload":predownload})
        else:
            data.update({'updatemode':'off'})
        self.SetUpgradeStrategy(json_to_asv(data))
    
    def change_upgrade_scheme_handler(self,scheme):
        def generate_time_output(input_data: dict) -> dict:
            """
            处理时间字段并生成指定格式的字典输出
            
            参数：
            input_data - 包含download_time和install_time的原始数据
            
            返回：
            动态生成的字典，仅包含有效数据项
            
            规则：
            1. 当download_time列表非空时，生成downloadtime字段（键名转换）
            2. 当install_time列表非空时，提取第一个元素的start_time生成installtime字段
            3. 空列表或不存在字段时自动过滤
            """
            output = {}
            
            # 处理download_time（键名转换逻辑）
            download_periods = [
                {"start": item["start_time"], "end": item["end_time"]}
                for item in input_data.get("download_time", [])
                if isinstance(item, dict) and "start_time" in item and "end_time" in item
            ]
            if download_periods:  # 仅当列表非空时生成字段
                output["downloadtime"] = download_periods
            
            # 处理install_time（首项提取逻辑）
            install_items = input_data.get("install_time", [])
            if install_items and isinstance(install_items[0], dict):  # 检查列表非空且首项有效
                first_install = install_items[0]
                if "start_time" in first_install:
                    output["installtime"] = first_install["start_time"]
            
            return output        
        configjsonstr=convert_a_sv_to_json(scheme)
        logging.info("upgrade scheme changed")
        logging.info(configjsonstr)
        json_data=json.loads(configjsonstr)
        data={}
        if(json_data.get('auto_download','true')=='true'):
            data.update({'updatemode':'autoupdate'})
            autoupdate=generate_time_output(json_data)
            period=int(json_data.get('download_period',{}).get('day',1))
            mode=json_data.get('mode','shutdown')
            autoupdate.update({"mode":mode}) 
            autoupdate.update({"period":period})
            data.update({"autoupdate":autoupdate})
        elif(json_data.get('auto_download','false')=='false'):
            data.update({'updatemode':'off'})
        self.SetUpgradeStrategy(json_to_asv(data))

    def auto_check_status_changed_handler(self, status):
        logging.info("is instance of bool:%s"%isinstance(status,bool))
        data={}
        if(status):
            data.update({"notification":bool(1)})    
        else:
            data.update({"notification":bool(0)})
        self.SetUpgradeStrategy(json_to_asv(data))

    def autoupgrade_status_changed_handler(self,autoupgradestate):
        logging.info("auto upgrade status changed:%s"%autoupgradestate)

    def autoupgrade_detail_changed_handler(self,mask,time):
        logging.info("auto upgrade detail changed:%s,%s"%(mask,time))

    def autoupgrade_period_changed_handler(self,day):
        logging.info("auto upgrade period changed:%d"%day)
        
    def upgrade_all_now_handler(self):
        logging.info("upgrade all now")
        data={}
        data.update({'updatemode':'updatenow'})
        self.SetUpgradeStrategy(json_to_asv(data))

    def run(self):
        logging.debug(_("Waiting for signal to start operation"))
        self.loop.run() 

if __name__ == "__main__":
    gettext.bindtextdomain("unattended-upgrades","/usr/share/locale")
    gettext.textdomain("unattended-upgrades")
    logdir = "/var/log/kylin-unattended-upgrades/"
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    logfile = os.path.join(logdir, "unattended-upgrades-shutdown.log")
    logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(message)s',datefmt='%Y-%m-%d,%H:%M:%S',level=logging.DEBUG,filename=logfile)
    signal.signal(signal.SIGTERM, signal_term_handler)
    unattendedupgrade=UnattendedUpgradeBackend()
    unattendedupgrade.init_dbus_connections()
    unattendedupgrade.init_config()
    unattendedupgrade.init_event_flags()
    unattendedupgrade.init_scheduler()
    unattendedupgrade.run()
   