ADD software version display

1. Problem Description

在手机拨号盘输入暗码*#xxx#,弹出对话框,显示手机各image版本号。

2. Analysis

已知条件:

  1. 手机各个分区版本已经写入到了工程源码的指定文件中(一般是放在development/version/include/version.inc文件中),其具体的形式如下所示:
    1. #define SCATTER_VER "K4H5EMMCCE00"
    1. #define PRELOADER_VER "P4H5H0H0CE00"
    1. #define UBOOT_VER "U4H5H0H0CE00"
    1. #define ANDROID_BOOT_VER "B4H5H0H0CE00"
    1. #define RECOVERY_VER "R4H5H0H0CE00"
    1. #define ANDROID_SYS_VER "Y4H5H0H0CE00"
    1. #define CACHE_VER "H4H5H0H0CE00"
    1. #define USRDATA_VER "S4H5H0H0CE00"
    1. #define LOGO_VER "L4H5H0H0CE00"
    1. #define FAT_VER "F4H5H0H0CE00"
    1. #define CUSTPACK_VER "C4H5HEU0CE00"
    1. #define SIMLOCK_VER "X4H0H0H0CE00"
    1. #define AP_DATABASE_VER "A4H5H0H0CE00"
    1. #define MODEM_DATABASE_VE "O4H5H0H0CE00"
    1. #define TRUSTZONE_VER "T4H5H0H0CE00"
  1. 输入暗码的dialog软件为第三方apk,该软件使用读系统属性的方法来获取版本信息,提供的系统属性接口如下:
    1. ro.tct.cust.ver /*保存custpack分区版本号的系统属性*/
    1. ro.tct.non.ver /*保存modem分区版本号的系统属性*/
    1. ro.tct.sys.ver /*保存system分区版本号的系统属性*/
    1. ro.tct.boot.ver /*保存boot分区版本号的系统属性*/
    1. ro.tct.preloader.ver /*保存preloader分区版本号的系统属性*/
    1. ro.tct.bootloader.ver /*保存bootloader分区版本号的系统属性*/
    1. ro.tct.reco.ver /*保存recovery分区版本号的系统属性*/

即当输入*#xxx#后,该apk会读取设备中的各个系统属性,然后将读到的值通过AlertDialog显示出来。

分析思路

既然已经知道了各个分区版本号所存储的文件位置,又知道三方apk读取信息的接口,那么需要做的就是:

  1. 读取文件version.inc中的数据;
  2. 将读到的值存入到对应的系统属性中;

问题似乎已经得到了解决,但是system、boot、preloader、bootloader、recovery分区的版本号在fota版本升级后会变化。也就是说通过上面的做法,将编译好的系统刷如设备中,该设备的各个分区版本号是永远不能改变的,那么上面的做法就无法满足版本升级后部分分区版本号改变的要求了。

因此,可以分为2种不同情况来处理,具体如下图所示:

  • 对于在fota升级前后不需要改变的版本号,直接读取该版本号,然后将它写入对应系统属性中。

  • 对于在fota升级前后需要改变的版本号,首先,将它读取出来写入到一个中间文件(对于boot和system版本号是存放.ver文件中,其他三个的版本号是放在对应的.img文件中特定位置并且加有标识字符)中,;然后,在手机每次启动时,动态的读取这个中间文件的值,并将读到的值写入对应系统属性中;如果有fota升级,在升级时会同时替换升级分区的中间文件,这样就可以保证每次显示版本号时,都是读取的最新版本号。

3. Solution

3.1 处理升级前后不改变的版本号

  1. 修改/build/core/Makefile文件

    从文件development/version/include/version.inc中分别读取modem分区版本号和custpack分区版本号,然后写入系统属性中,具体代码如下:
    1. VERSIONDEF := development/version/include/version.inc #引进version.inc文件
    1. VERSIONLEN := 12
    1. CUSTPACK_VER := $(shell awk ' /CUSTPACK_VER/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))
    1. MODEM_DATABASE_VE := $(shell awk ' /MODEM_DATABASE_VE/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))
    1. $(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)"
    1. ......
    1. CUSTPACK_VER="$(CUSTPACK_VER)" \
    1. MODEM_DATABASE_VE="$(MODEM_DATABASE_VE)" \
    1. ......
  1. 修改文件build/tools/buildinfo.sh
    1. echo "ro.tct.cust.ver=$CUSTPACK_VER"
    1. echo "ro.tct.non.ver=$MODEM_DATABASE_VE“

到现在modem分区版本号和custpack分区版本号已经写入到文件out目录下build.prop文件中,通过打开该文件可以看到从version.inc中读到的版本号。

3.2 处理升级前后改变的版本号

  • 修改/build/core/Makefile文件
    1. #write boot version to boot.ver
    1. if [ -f $(TOPDIR)$(VERSIONDEF) ];#将boot分区版本号写入到临时文件boot.ver
    1. then
    1. awk ' /ANDROID_BOOT_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(TOPDIR)$(VERSIONDEF) > $(TARGET_ROOT_OUT)/boot.ver ;
    1. fi
    1. #write boot version to boot.ver
    1. ......
    1. define build-systemimage-target
    1. #write system version to system.ver
    1. if [ -f $(TOPDIR)$(VERSIONDEF) ];#将system分区版本号写入到临时文件boot.ver
    1. then
    1. awk ' /ANDROID_SYS_VER/ { print substr($$NF, 2,$(VERSIONLEN)) }' $(TOPDIR)$(VERSIONDEF) > $(TARGET_OUT)/system.ver;
    1. fi
    1. #write system version to system.ver
    1. #write recovery version and the string of JRD_VERSION_MARK_ to recovery.img
    1. RECOVER_VERSION = JRD_VERSION_MARK_$(shell awk ' /RECOVERY_VER/ { print substr($$NF, 2,$(VERSIONLEN) ) }' $(TOPDIR)$(VERSIONDEF))
    1. # Assumes this has already been stripped
    1. ifdef BOARD_KERNEL_CMDLINE
    1. INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE) $(RECOVER_VERSION)"
    1. else
    1. INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(RECOVER_VERSION)"
    1. Endif
    1. #write recovery version and the string of JRD_VERSION_MARK_ to recovery.img
  • 修改文件/vendor/mediatek/proprietary/bootable/bootloader/lk/Android.mk在指定头文件中定义一个宏定义,编译后再打开该version.h文件,可以看到如#define LK_VER JRD_VERSION_MARK_U4HB3004CE00样子的宏定义。
    1. VERSIONDEF := development/version/include/version.inc
    1. VERSIONLEN := 12
    1. if [ -f $(LK_ROOT_DIR)/$(VERSIONDEF) ];
    1. then
    1. echo -n "#define LK_VER JRD_VERSION_MARK_" > $(LK_ROOT_DIR)/vendor/mediatek/proprietary/bootable/bootloader/lk/include/version.h ;
    1. awk ' /UBOOT_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(LK_ROOT_DIR)/$(VERSIONDEF) >> $(LK_ROOT_DIR)/vendor/mediatek/proprietary/bootable/bootloader/lk/include/version.h ;
    1. fi
  • 修改文件vendor/mediatek/proprietary/bootable/bootloader/lk/kernel/main.c,将前面的宏定义LK_VER写入到对应的.img文件中去。
    1. #include "version.h"
    1. ststatic int bootstrap2(void *arg)
    1. {
    1. ......
    1. #ifdef LK_VER
    1. #define xstr(s) str(s)
    1. #define str(s) #s
    1. printf("%s\n", xstr(LK_VER));/*大概是只要使用了该宏,就会为该宏分配存储空间并存储在目标文件img中*/
    1. #endif
    1. ......
    1. }
  • 修改文件vendor/mediatek/proprietary/bootable/bootloader/preloader/Android.mk在指定头文件中定义一个宏定义,编译后再打开该version.h文件,可以看到如#define PRELOADER_VER JRD_VERSION_MARK_P4HB3004CE00样子的宏定义。
    1. VERSIONDEF := development/version/include/version.inc
    1. VERSIONLEN := 12
    1. if [ -f $(PRELOADER_ROOT_DIR)/$(VERSIONDEF) ];
    1. then
    1. echo -n "#define PRELOADER_VER JRD_VERSION_MARK_" > $(PRELOADER_DIR)/platform/mt6735/src/core/inc/version.h ;
    1. awk ' /PRELOADER_VER/ { print substr($$NF, 2, $(VERSIONLEN)) }' $(PRELOADER_ROOT_DIR)/$(VERSIONDEF) >> $(PRELOADER_DIR)/platform/mt6735/src/core/inc/version.h ;
    1. fi
  • 修改文件vendor/mediatek/proprietary/bootable/bootloader/preloader/platform/mt6735/src/core/main.c,将前面的宏定义PRELOADER_VER写入到对应的.img文件中去。
    1. #include "version.h"
    1. static void bldr_pre_process(void)
    1. {
    1. ......
    1. #ifdef PRELOADER_VER
    1. #define xstr(s) str(s)
    1. #define str(s) #s
    1. printf("%s\n", xstr(PRELOADER_VER));
    1. #endif
    1. ......
    1. }
  • 修改文件vendor/jrdcom/proprietary/traceability/main.c,将.ver文件中的版本号读取出来,然后写入相应的系统属性.ro中
    1. #include <stdio.h>
    1. #include <string.h>
    1. #include <errno.h>
    1. #include <stdlib.h>
    1. #include <sys/types.h>
    1. #include <fcntl.h>
    1. #include <unistd.h>
    1. #define __ALLOCATE_CFG_AUDIO_DEFAULT_H
    1. #include "libnvram.h"
    1. #include "CFG_PRODUCT_INFO_File.h"
    1. #include "Custom_NvRam_LID.h"
    1. #include <cutils/properties.h>
    1. //#define printf(x...) do { KLOG_ERROR("!!!!!!!!!!!!!!", x); } while (0)
    1. #include <android/log.h>
    1. #define LOG_TAG "gettra"
    1. #define printf(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    1. //VERSION parameter
    1. #define VERSION_FILE_PATCH "/system/system.ver"
    1. #define VERSION_OFFSET 0
    1. #define VERSION_SIZE 12
    1. #define VERSION_ANBOOT_FILE_PATCH "/boot.ver"
    1. int main(int argc, char *argv[])
    1. {
    1. int fid_ver;
    1. unsigned char ver[13]={0};
    1. unsigned char anbootver[13]={0};
    1. int fid_anbootver;
    1. memset(ver, 0x00, 12);
    1. memset(anbootver, 0x00, 12);
    1. //get version and set ro.tct.sys.ver
    1. fid_ver = open( VERSION_FILE_PATCH, O_RDONLY);
    1. if(fid_ver < 0)
    1. {
    1. perror("open:error");
    1. }
    1. else
    1. {
    1. lseek(fid_ver, VERSION_OFFSET , SEEK_SET);
    1. read(fid_ver, ver, VERSION_SIZE);
    1. close(fid_ver);
    1. }
    1. fid_anbootver = open( VERSION_ANBOOT_FILE_PATCH, O_RDONLY);
    1. if(fid_anbootver < 0)
    1. {
    1. //perror("open:error");
    1. printf("fid_anbootver error\n");
    1. }
    1. else
    1. {
    1. printf("fid_anbootver \n");
    1. lseek(fid_anbootver, VERSION_OFFSET , SEEK_SET);
    1. read(fid_anbootver, anbootver, VERSION_SIZE);
    1. close(fid_anbootver);
    1. }
    1. property_set("ro.tct.sys.ver", ver);
    1. property_set("ro.tct.boot.ver", anbootver);
    1. printf("gettracability exe over!\n");
    1. return 1;
    1. }
    1. #undef __ALLOCATE_CFG_AUDIO_DEFAULT_H
  • 修改文件vendor/jrdcom/proprietary/version/main.c,将.img文件中的版本号读取出来,然后写入相应的系统属性.ro中
    1. /*
    1. * Get version number from raw partition image, output to one file.
    1. */
    1. #include <stdio.h>
    1. #include <string.h>
    1. #include <errno.h>
    1. #include <sys/types.h>
    1. #include <sys/stat.h>
    1. #include <fcntl.h>
    1. #include <unistd.h>
    1. #include <sys/system_properties.h>
    1. #include <utils/Log.h>
    1. #define printf ALOGE
    1. #define PRELOADER "/dev/block/mmcblk0boot0"
    1. #define BOOTLOADER "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/lk"
    1. #define RECOVERYIMG "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/recovery"
    1. #define PRELOADER_SIZE 0x40000
    1. #define BOOTLOADER_SIZE 0x60000
    1. #define RECOVERYIMG_SIZE 0x900000
    1. #define JRD_VERSION_MARK "JRD_VERSION_MARK_"
    1. #define JRD_VERSION_MARK_LEN 17
    1. #define JRD_VERSION_LEN 12
    1. static char buff[RECOVERYIMG_SIZE];
    1. /*这个方法就是先从指定img文件中读size个字节到一个临时数组ver中,然后根据size的大小,将数组中的值存入相应的系统属性.ro文件中*/
    1. int getver(char *img, int size)
    1. {
    1. int ret = 0;
    1. char ver[JRD_VERSION_MARK_LEN + JRD_VERSION_LEN + 1];
    1. char setver[JRD_VERSION_MARK_LEN + JRD_VERSION_LEN + 1]={'\0'};
    1. int f;
    1. ssize_t s;
    1. int p;
    1. /*为数组分配存储空间大小为 17+12+1*/
    1. memset(ver, 0, JRD_VERSION_MARK_LEN);
    1. /*先讲表示字段"JRD_VERSION_MARK_"拷贝到数组中*/
    1. strncpy(ver, JRD_VERSION_MARK, JRD_VERSION_MARK_LEN);
    1. // read image
    1. f = open(img, O_RDONLY);
    1. if (f < 0) {
    1. printf("can not open image!!!!!!!!!!!!!!!!!!\n");
    1. return f;
    1. }
    1. /*将.img文件中的前size个字符拷贝到buff中*/
    1. s = read(f, buff, size);
    1. printf("read out byte cnt: %d!!!!!!!!!!!!!!!!!!\n", s);
    1. f = close(f);
    1. // search the mark string
    1. for(p = 0; p < size; p++) {
    1. // match the first char
    1. if (buff[p] == ver[0]) {
    1. int t;
    1. for(t = 0; t < JRD_VERSION_MARK_LEN; t++) {
    1. if(buff[p + t] != ver[t])
    1. break;
    1. }
    1. // match whole mark string
    1. if(t == JRD_VERSION_MARK_LEN)
    1. break;
    1. }
    1. }
    1. // not find
    1. if (p == size)
    1. return ret;
    1. /*p + strlen(JRD_VERSION_MARK)为版本号在buff中的偏移位*/
    1. // find it
    1. strncpy(ver, buff + p + strlen(JRD_VERSION_MARK), JRD_VERSION_LEN);
    1. ver[JRD_VERSION_LEN] = '\n';
    1. if(size == PRELOADER_SIZE)
    1. {
    1. strncpy(setver,ver, JRD_VERSION_LEN);
    1. setver[JRD_VERSION_LEN] = '\0';、
    1. /*将版本号写入系统属性ro.tct.preloader.ver中*/
    1. if(property_set("ro.tct.preloader.ver", setver) <0)
    1. {
    1. printf("can not set ro.tct.preloader.ver!!!!!!!!!!!!!!!\n");
    1. }
    1. }else if (size == BOOTLOADER_SIZE){
    1. strncpy(setver,ver, JRD_VERSION_LEN);
    1. setver[JRD_VERSION_LEN] = '\0';
    1. /*将版本号写入系统属性ro.tct.bootloader.ver中*/
    1. if(property_set("ro.tct.bootloader.ver", setver) <0)
    1. {
    1. printf("can not set ro.tct.bootloader.ver!!!!!!!!!!!!!!!\n");
    1. }
    1. }else{
    1. strncpy(setver,ver, JRD_VERSION_LEN);
    1. setver[JRD_VERSION_LEN] = '\0';
    1. /*将版本号写入系统属性ro.tct.reco.ver中*/
    1. if(property_set("ro.tct.reco.ver", setver) <0)
    1. {
    1. printf("can not set ro.tct.reco.ver!!!!!!!!!!!!!!!\n");
    1. }
    1. }
    1. return ret;
    1. }
    1. int main(int argc, char *argv[])
    1. {
    1. int ret = 0;
    1. printf("PRELOADER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    1. ret = getver(PRELOADER, PRELOADER_SIZE);
    1. printf("BOOTLOADER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    1. ret = getver(BOOTLOADER, BOOTLOADER_SIZE);
    1. printf("RECOVERYIMG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    1. ret = getver(RECOVERYIMG, RECOVERYIMG_SIZE);
    1. return ret;
    1. }

到现在基本改完了,然后编译系统并将编好系统刷入设备中,验证发现无法显示出所有的版本号。难到上面的分析有问题?通过log分析发现,问题出在SELinux权限的方面。那什么是SELinux呢?说简单点,就是针对某个进程,它是否具有访问某个文件资源的权限。而在上面运行vendor/jrdcom/proprietary/version/main.c代码的进程需要访问lk、preloader、recovery这个3个分区的文件资源。我们需要将该进程读这3个分区的权限放开,才能获得分区中的版本号,不然无法获取版本号的值。

需要注意的是要具体查看项目中到底使用到哪个目录下的sepolicy规则,可以通过查看BoardConfig.mk文件来确定

    1. #SELinux Policy File Configuration
    1. ifeq ($(strip $(MTK_BASIC_PACKAGE)), yes)
    1. BOARD_SEPOLICY_DIRS += \
    1. device/mediatek/mt6735/sepolicy/basic
    1. endif
    1. ifeq ($(strip $(MTK_BSP_PACKAGE)), yes)
    1. BOARD_SEPOLICY_DIRS += \
    1. device/mediatek/mt6735/sepolicy/basic \
    1. device/mediatek/mt6735/sepolicy/bsp
    1. endif
    1. ifneq ($(strip $(MTK_BASIC_PACKAGE)), yes)
    1. ifneq ($(strip $(MTK_BSP_PACKAGE)), yes)
    1. BOARD_SEPOLICY_DIRS += \
    1. device/mediatek/mt6735/sepolicy/basic \
    1. device/mediatek/mt6735/sepolicy/bsp \
    1. device/mediatek/mt6735/sepolicy/full
    1. endif
    1. endif

具体的selinux规则修改如下:

  • 在文件[sepolicy规则目录]/file.te中定义一个type,具体如下:
    1. type getver_data_file, file_type;
  • 在文件[sepolicy规则目录]/device.te中定义一个type,具体如下:
    1. type lk_block_device, dev_type;
  • 在文件[sepolicy规则目录]/file_contexts中定义定义文件的安全上下文,具体如下:
    1. #lk文件的安全上下文
    1. /dev/block/platform/mtk-msdc\.0/[0-9]+\.msdc0/by-name/lk u:object_r:lk_block_device:s0
    1. #getver文件的安全上下文
    1. /system/bin/getver u:object_r:getver_exec:s0
    1. /data/imgver u:object_r:getver_data_file:s0
  • 在文件[sepolicy规则目录]下创建一个getver.te文件,具体如下:
    1. # getver
    1. #为getver应用定义域
    1. type getver, domain;
    1. #定义一个getver_exec类型的type
    1. type getver_exec, exec_type, file_type;
    1. init_daemon_domain(getver)
    1. # Give getver a place where only getver can store files; everyone else is off limits
    1. file_type_auto_trans(getver, system_data_file, getver_data_file)
    1. #定义getver应用对文件的访问规则
    1. allow getver getver_data_file:file {create rw_file_perms};
    1. allow getver block_device:file {open rw_file_perms};
    1. allow getver block_device:dir {search rw_file_perms};
    1. allow getver nvram_data_file:file {open rw_file_perms};
    1. allow getver nvram_data_file:lnk_file {r_file_perms};
    1. allow getver nvram_data_file:dir {search r_file_perms};
    1. allow getver nvdata_file:dir {search rw_file_perms};
    1. allow getver userdata_block_device:blk_file {rw_file_perms};
    1. allow getver preloader_block_device:blk_file {r_file_perms};
    1. allow getver recovery_block_device:blk_file {r_file_perms};
    1. allow getver lk_block_device:blk_file {open r_file_perms};
    1. #allow getver system_data_file:dir {create search rw_file_perms add_name};
    1. allow getver getver_data_file:file {create open read write};
    1. #added by xuanfeng.ye for Task1304435 begin
    1. allow getver system_file:file {execute_no_trans};
    1. #added by xuanfeng.ye for Task1304435 end
    1. #20170204-jrdhz-yaosen.lin-add-for-t3695219-to-show version-brand
    1. allow getver property_socket:sock_file { write };
    1. allow getver system_prop:property_service { set };
    1. allow getver init:unix_stream_socket { connectto };

经过上面的修改,运行vendor/jrdcom/proprietary/version/main.c代码的进程就可以读取img文件中的版本号,并且将版本号写入对应的系统属性中了。而三方dialog apk就可以通过读取对应的系统属性来在界面上显示各个版本号了,对于系统属性读写需不需要加selinux访问规则就要看项目具体的配置了,如果项目对系统属性也使用了selinux,则要对系统属性加上对应selinux规则才能正确显示版本号。

4. Summary

这做这个defect的时候,需要区分软件升级前后要改变的版本号和不需要改变的版本号,对应升级前后一直不变的版本号,只需要将它们读出来,静态的写入到文件中,在需要使用的时候再拿出来就可以了。而对应升级前后需要改变的版本号,可以通过中间文件的形式来处理即在每次开机的时候通过读取中间文件来获取版本号,同时在软件升级时候同时更新中间文件来确保版本号也一起更新过,而在做这个问题的时候,遇到的难点则是selinux部分,因为对selinux的知识也不是很熟悉,写的selinux规则也是照猫画虎将别人的搬过来,而没有实际理解它们的含义,所以导致编译N次都没有成功的读取出img部分的版本号。整个问题的解决思路还是比较简单的,特别需要的就是各个地方的细节,比如最开始写的selinux规则,不管怎么写都没有效果,最后发现原来是使用的那个sepolicy目录根据就没有编译进系统。

ADD software version display的更多相关文章

  1. cordova platform add specified version

    cordova platform add specified version 命令格式 cordova platform add android@4.0 可用的版本 Valid install tar ...

  2. 【转】System.Data.OracleClient requires Oracle client software version 8.1.7 or greater

    安装完ASP.NET,Oracle9i客户端后,使用System.Data.OracleClient访问Oracle数据库如果出现这种错误:System.Data.OracleClient requi ...

  3. Software Version

    Software Version Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) To ...

  4. C# System.Data.OracleClient requires Oracle client software version 8.1.7 or greater

    好好的程序,突然出现了错误,原因是:System.Data.OracleClient requires Oracle client software version 8.1.7 or greater, ...

  5. Eclipse执行import命令导入maven项目时报错:Add a version or custom suffix using "Name template" in "Advanced" settings

    新建了两个maven项目在E盘workspace目录,后面移到workspace/app_engine目录下提交svn,再通过Eclipse的File->import导入时报错了: Projec ...

  6. Data Base System.Data.OracleClient requires Oracle client software version 8.1.7 or greater解决方案

    System.Data.OracleClient requires Oracle client software version 8.1.7 or greater解决方案 一.问题: 1.通过Syst ...

  7. System.Data.OracleClient requires Oracle client software version 8.1.7 or greater

    It is a security issue, so to fix it simply do the following: Go to the Oracle folder. 1- Right Clic ...

  8. HDOJ(HDU) 1976 Software Version(简单判断)

    Problem Description 相信大家一定有过在网上下载软件而碰到多个不同版本的情况. 一般来说,软件的版本号由三个部分组成,主版本号(Major Version Number),子版本号( ...

  9. Software Version --hdu1976

    #include using namespace std; int main() { int T; cin>>T; int a1,b1,c1; int a2,b2,c2; while(T- ...

随机推荐

  1. jdk1.8安装教程

    JDK1.8安装包下载 链接:https://pan.baidu.com/s/18pEMo3gYsAAHWC9DjizP1A 提取码:xu99 1.双击JDK1.8的安装包,并点击下一步 2.选择安装 ...

  2. Python3.6+Django2.0以上 xadmin站点的配置和使用

    1. xadmin的介绍 django自带的admin站点虽然功能强大,但是界面不是很好看.而xadmin界面好看,功能更强大,并完全支持Bootstrap主题模板.xadmin内置了丰富的插件功能. ...

  3. CF20B Equation 题解

    Content 解方程 \(ax^2+bx+c=0\). 数据范围:\(-10^5\leqslant a,b,c\leqslant 10^5\). Solution 很明显上求根公式. 先来给大家推推 ...

  4. MVC中的打印功能

    HTML页面: @{ Layout = "~/Views/Shared/_IframeLayout.cshtml";}@Scripts.Render(ViewBag.ScriptP ...

  5. 以太网/ IPV4/IPV6包头,TCP包头格式回顾

    问题:以太网数据包,承载的数据内容大小46~1500字节,是如何来的? 以太网数据包结构  以太网协议规定最小链路层数据包(帧)为64字节,其中以太网首部+尾部共计18字节(源/目的MAC12字节:上 ...

  6. Vue3 全家桶,从 0 到 1 实战项目,新手有福了

    前端发展百花放,一技未熟百技出.未知何处去下手,关注小编胜百书. 我是前端人,专注分享前端内容! 本篇文章主要是,使用 vite 创建一个vue3 项目,实践 vie-router4 vuex4 结合 ...

  7. JAVA根据URL生成二维码图片、根据路径生成二维码图片

    引入jar包 zxing-2.3.0.jar.IKAnalyzer2012_u6.jar 下载地址:https://yvioo.lanzous.com/b00nlbp6h                ...

  8. 基于内存的关系数据库memsql初探

    背景 广告系统中,算法模型预估需要根据广告的实时转化统计结果,才能做出更精准的预估:同时,支持多维度聚合查询(例如按照广告各个不同层级维度,按照时间不同粒度的维度),并跨大区合并.一开始的版本是基于m ...

  9. 第二十八个知识点:什么是公钥密码学的IND-CCA安全定义?

    第二十八个知识点:什么是公钥密码学的IND-CCA安全定义? 我们将在这篇博客中讨论公钥加密的IND-CCA安全. IND-CCA安全代表选择明文的不可伪造性.这样的安全方案的思想就是给定一个密文,攻 ...

  10. 「Codeforces 468C」Hack it!

    Description 定义 \(f(x)\) 表示 \(x\) 的各个数位之和.现在要求 \(\sum_{i=l}^rf(i)\bmod a\). 显然 ans=solve(l,r)%a; if(a ...