首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

recovery的一些开发点滴

Android的recovery是我在公司做的最多的,应该也是我在Android中了解的较为深入的一个部分。recovery这部分其实Android本身都已经提供了很完善的一套机制,但是因为公司是做机顶盒的,所以在因为平台订制的关系,recovery这部分还是做了很多修改的。首先,修改的比较少的是OTT这种盒子,本次也主要讲这种,其实这种和手机区别不大。而类似将DVB 中的loader和Android的recovery整合到一起这种,确实比较不一样。例如在我们公司,整个升级的签名校验订制以及升级所用到的update.zip包中的烧录进程就都是自己一套的。但是万变不离其中,其实了解了就发现其实也都是那样。 所以简单讲下recovery的相关知识: 如何进入recovery 标准Android的recovery进入方式一般是这几种”在设置中点击恢复出厂设置“”开机按组合件进入recovery“,例如 home + power(这种是手机的)”系统检测到固件更新,下载后要求你重启,这时重启会先进入recovery“其实这几种在实现上是差不多:1、首先,我们要明白,在Android中,其实是由两个系统存在的,recovery,其实就是一个小系统,专门用来刷机的。2、具体是要进入哪个系统,这个是又fastboot来决定的(因为你基本可以认为他就是开机的第一个程序),他决定进那个就哪个。当然,这个也是由我们来告诉fastboot的。3、怎么告诉呢?有两种方式。第一种,就是按组合键,(就相当于,我们告诉fastboot,开机检测到有人按了这个组合件就是进recovery,别进安卓。这个当然就可以定制了,例如在机顶盒,我们通常是改成按遥控器,如”连续按上中下键“)。第二种,就是Android告诉fastboot下次启动进recovery,然后Android自己再重启。这个就关系到了另一个分区,叫MISC(你基本可以认为他就是存储recovery命令的)。因为fastboot启动会去读这个MISC分区中的内容,来决定自己进哪个系统。系统固件升级和恢复出厂都属于这种,就是recovery命令有点不一样。 recovery流程 首先要知道一点,recovery系统是一个类似Linux的变种,使用了Android init的那一套,但是不会进去虚拟机。所以init.rc和property那些Android的东西对于recovery还是一样的。 流程如下:1、一般来说,在init.reovery.rc中,就可以看到启动了/sbin/recovery,这就启动了recovery来作为一个service2、recovery进程简介: 1. load_volume_table();这个函数从”/etc/recovery.fstab”读取分区信息2. get_arg():主要就是获取recovery的命令、参数等等,这样recovery进程才知道自己要做什么,升级包在哪,这些都以一定的结构体保存在MISC分区中,我们成为bootloader_message,也就是下面会说的BCB。 ①get_bootloader_message():主要工作是根据分区的文件格式类型(mtd或emmc)从MISC分区中读取BCB数据块到一个临时的变量中。(get和set bootloader_message:1、/从”/misc”读取分区设置/2、/mtd类型只读取或者修改MISC_COMMAND_PAGE一页/3、/emmc类型直接对一个device进行读写操作/) ②然后开始判断Recovery服务是否有带命令行的参数(/sbin/recovery,根据现有的逻辑是没有的),若没有就从BCB中读取 recovery域。如果读取失败则从/cache/recovery/command中读取然后写入BCB临时变量。这样这个BCB的临时变量中的recovery域就被更新了。在将这个BCB的临时变量写回真实的BCB之前,又更新的这个BCB临时变量的command域为“boot-recovery”。这样做的目的是如果在升级失败(比如升级还未结束就断电了)时,系统在重启之后还会进入Recovery模式,直到升级完成。 ③在这个BCB临时变量的各个域都更新完成后使用set_bootloader_message()写回到真正的BCB块中。这个过程可以用一个简单的图来概括,这样更清晰:(get_arg()这个函数中,主要是获取参数,重写recovery命令到BCB。但是,有时从command_file,有时从BCB读取。看如何从上层进入recovery,从上层重启进入recovery的话,会将recovery命令写入到BCB,将升级包目录写进command_file。也就是说,command_file是不会有recovery标识的。) // --> write the arguments we have back into the bootloader control block // always boot into recovery after this (until finish_recovery() is called) strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); 1 2 3 4 1 2 3 4 (所以说,从BCB读出,在写回,主要就是修改这两句话。这样子,就能保证进入升级。要注意的是,进入升级模式,是在fastboot的过程选择的,而这里是为了保证升级过程中若中断了,下次还是进recovery。第二种是如果BCB读取失败 还可以从command file中去读取。) 接下来就是判断从上面流程获取的recovery命令及参数了3. if(update_package):判断update_package是否有值,若有就表示需要升级更新包,此时就会调用 install_package()。在这一步中将要完成安装实际的升级包。这是最为复杂,也是升级update.zip包最为核心的部分。(这种就是所谓的固件升级)4. if(wipe_data/wipe_cache):这一步判断实际是两步,在源码中是先判断是否擦除data分区(用户数据部分)的,然后再判断是否擦除cache分区。值得注意的是在擦除data分区的时候必须连带擦除cache分区。在只擦除cache分区的情形下可以不擦除data分区。(这种就所谓的恢复出厂设置) finish_recovery():这是Recovery关闭并进入Main System的必经之路。其大体流程如下:① 将intent(字符串)的内容作为参数传进finish_recovery中。如果有intent需要告知Main System,则将其写入/cache/recovery/intent中。这个intent的作用尚不知有何用。② 将内存文件系统中的Recovery服务的日志(/tmp/recovery.log)拷贝到cache(/cache/recovery/log)分区中,以便告知重启后的Main System发生过什么。③ 擦除MISC分区中的BCB数据块的内容,以便系统重启后不在进入Recovery模式而是进入更新后的主系统。④ 删除/cache/recovery/command文件。这一步也是很重要的,因为重启后Bootloader会自动检索这个文件,如果未删除的话又会进入Recovery模式。原理在上面已经讲的很清楚了。 install_package() 上面已经说过,这个基本是整个recovery最复杂的也是最核心的部分,就是他完成刷机(固件升级)。详细说下: ①ensure_path_mount():先判断所传的update.zip包路径所在的分区是否已经挂载。如果没有则先挂载。②load_keys():加载公钥源文件,路径位于/res/keys。(下面讲)③verify_file():对升级包update.zip包进行签名验证。(下面讲)④mzOpenZipArchive():打开升级包,并将相关的信息拷贝到一个临时的ZipArchinve变量中。这一步并未对我们的update.zip包解压。⑤try_update_binary():在这个函数中才是对我们的update.zip升级的地方。这个函数一开始先根据我们上一步获得的zip包信息,以及升级包的绝对路径将 update_binary文件拷贝到内存文件系统的/tmp/update_binary中。以便后面使用。⑥pipe():创建管道,用于下面的子进程和父进程之间的通信。父进子出。⑦fork():创建子进程。其中的子进程主要负责执行binary(execv(binary,args),即执行我们的安装命令脚本),父进程负责接受子进程发送的命令去更新ui显示(显示当前的进度)。子父进程间通信依靠管道。⑧其中,在创建子进程后,父进程有两个作用。一是通过管道接受子进程发送的命令来更新UI显示。二是等待子进程退出并返回INSTALL SUCCESS。其中子进程在解析执行安装脚本execv(binary,args)的作用就是去执行binary程序,这个程序的实质就是去解析update.zip包中的 updater-script脚本中的命令并执行。由此,Recovery服务就进入了实际安装update.zip包的过程。 实际上,上面已经说完了主要流程,其实也比较简单,所以接下来做一点细节的补充: 细节补充 Install_package()中load_keys和verify_file/返回key和key的个数,key的位置在 “/res/keys”/1.RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);key的结构如下:*{key->len,key->n0inv,{key->n[i]},{key->rr[i]}}*或者v2 {key->len,key->n0inv,{key->n[i]},{key->rr[i]}}*example”{64,0xc926ad21,{1795090719,…,-695002876},{-857949815,…,1175080310}}”“v2 {64,0xc926ad21,{1795090719,…,-695002876},{-857949815,…,1175080310}}”/Key的版本不同的话,幂分别是3和65537 /*对zip包数据进行校验。对zip包的签名部分进行摘要计算(sha),再利用key对摘要2.err = verify_file(path, loadedKeys, numKeys); Zip包结构1:主要数据,已经经过签名2:end-of-central-directory 包括comment_size + EOCD_HEADER_SIZE其中(eocd[0] = 0x50 eocd[1] = 0x4b eocd[2] = 0x05 eocd[3] = 0x06)(用于指纹校验)RSA块:经秘钥加密,可用于签名校验。3:footer:(2-byte signature start)ffff (2-byte comment size)其中comment_size = footer[4] + (footer[5] << 8);eocd_size = comment_size + EOCD_HEADER_SIZE;signature_start = footer[0] + (footer[1] << 8);signature_start - FOOTER_SIZE这个大小用来存放RSA block.(这算一步小校验)signed_len:except for the comment length field (2 bytes) and the comment data. 利用上面几个固定的字节对应的固定的值,可以进行指纹校验,这是第一步和第二步的校验第三步校验504b0506 若出现在正确的位置后面的话,则”EOCD marker occurs after start of EOCD第四步校验就是对/zip包的前部分,SHA_update(&ctx, buffer, size);摘要计算/过程SHA_init(&ctx);SHA_update(&ctx, buffer, size);//一次会处理4096字节const uint8_t* sha1 = SHA_final(&ctx);得到摘要结果/利用公钥 对摘要进行校验,上一步得到的/RSA_verify() Zip包的后面是RSA区和6个字节的脚信息,RSA区是明文用私钥加密后的数据,机顶盒中会有一个公钥。先对前面的升级数据进行SHA1,然后用公钥对RSA区数据进行解密。解密后的数据的前半部进行 pkcs1.5 padding bytes.校验。解密后的数据的后半部和SHA1后的数据进行比较,完成校验。(20个字节) updater-script脚本部分函数说明升级脚本文件updater-script的内容可根据自己需要进行修改。对脚本中的部分函数进行简要说明: z ui_print(char *str)功能:打印信息。参数:str指针指向要打印的信息地址。 z show_progress (char *sec,char *total)功能:显示进度条。参数:− sec:多少秒更新一次进度条,一般为1。− total:升级所耗时间(根据升级包大小来确定)。 z format(charfs_type, char *partition_type,char *location,charfs_size, char *mount_point)功能:格式化分区。参数:− fs_type:文件系统类型(“ubifs”or“ext4”,“raw”)。¾ NAND Flash器件:裸分区:raw;文件系统分区:ubifs。¾ eMMC器件:裸分区:不支持;文件系统分区:ext4。− partition_type:器件类型(“MTD”or“EMMC”)。− location:分区名或者分区对应的设备节点。¾ NAND Flash器件,分区名:system。¾ eMMC器件分区,对应的设备节点: /dev/block/platform/hi_mci.1/byname/syste。− fs_size:0表示擦除整个分区。− mount_point:分区挂载点。 z package_extract_file(char *package_path, char *destination_path)功能:从zip包中提取单个文件。参数:− package_path:解压的文件。− destination_path:解压到的目标路径。 z write_raw_image (char *file, char *partition)功能:将单个文件写入分区。参数:− file:欲写入的文件。− partition:欲写入的分区。 z mount(char *fs_type, char *partition_type, char *location, char *mount_point)功能:挂载特定分区到某目录下参数:− fs_type:文件系统类型(“ubifs”or “ext4”)。− partition_type:器件类型(“MTD”or “EMMC”)。− location:分区名字或者分区对应的设备节点。¾ NAND Flash器件,分区名:system¾ eMMC器件分区,对应的设备节点:/dev/block/platform/hi_mci.1/by-name/system− mount_point:挂载点。 z unmount(char *mount_point)功能:卸载分区。参数:− mount_point:分区挂载点。 z package_extract_dir(char *package_path, char *destination_path)功能:直接提取一文件夹并直接解压到相应目录。参数:− package_path:zip压缩包里面要提取的文件夹名。− destination_path:解压到的目录。 z symlink(char *name, char *argv[])功能:将argv* 指向的内容全部链接到name文件。参数:− name:想要链接到的文件名。− argv:想要链接的文件。 z set_perm_recursive (int uid, int gid, int dir_mode, int file_mode,char *path)功能:修改目录权限及目录内文件的权限。参数:− uid:用户id。− gid:组 id。− dir_mode:目录权限。− file_mode:目录内文件权限。− path:目录路径。 z partchange(char *partition_type, char *new_partition)功能:依据传入的分区信息,在内核中建立新的分区参数:− partition_type:器件类型(“MTD”or “EMMC”)− new_partiton:分区信息− EMMC器件:关键字是:dev/block/mmcblk0− MTD器件:关键字是:hinandpartchange函数不支持spi器件,支持Nand Flash,eMMC器件 z setmisc(char *partition_type, char *location)功能:写misc标记位− partition_type:器件类型(“MTD”or“EMMC”)− location:分区名字或者分区对应的设备节点。¾ NAND Flash器件,分区名:misc¾ eMMC器件分区,对应的设备节点:/dev/block/platform/hi_mci.1/by-name/misc 增量升级及升级包的制作 很多时候,我们要升级的固件和上一个版本差的只是一两个APK或者是多了一些库文件,这个时候,如果我们再升级这个system分区,即升级整个system.img就做了很多务必要的工作,而且耗费的流量太大。从上面的升级脚本看到,其实完全是可以将某个文件\目录按照指定的属性添加到指定的目录下的,同时也可以删除掉某个指定的文件\目录。这个就是增量升级。本来,在Android源码中./build/tools/releasetools/ota_from_target_files -n -i <旧包> <新包> <差分包名> 是可以制作OTA增量升级包的,但是一般,不会这么干,因为这种做法太蠢了。那怎么做呢,从上面的一大堆话中,其实可以知道升级就是按照按照升级脚本来的。所以,升级包(rom包)制作方式:1、改一个自己需要的升级脚本,可以试增量升级,也可以是整个镜像升级。(当然脚本还是放在哪个目录下,然后update-binary也得支持这些脚本命令才行)2、然后把要的东西(APK,库,镜像)和升级脚本打包成一个update.zip,在用源码中的key给这个升级包进行签名,然后就做成一个可以用的升级包了。(当然了,手机刷机常用的rom包,其实也是一样的,不过这个时候就是升级整个system.img,或者根据需要再升级某些指定的分区。)怎么签名:Java-jar out/host/linux-x86/framework/signapk.jar -w build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8 ~/export/update_signed.zip ~/export/updatesigned.zip 之前讲过Android中recovery的基本知识。在工作中,需要做的经常是对标准recovery做一些定制化,所以这篇文章,记录下这段时间的一些心得: 1、增量升级: 在源码根目录下自行make otapackage 会生成升级包,两次编译后包用下面工具:./build/tools/releasetools/ota_from_target_files -n -i <旧包> <新包> <差分包名> ,可以制作增量升级包。这里必须用中间生成的包才行。改进方法:前面说的可以自己写一个简单的Linux脚本,把修改后的升级脚本和文件进行打包签名,这样可以比源码中直接make otapackage效率要高一些,同时也更灵活。有时你这个版本只是多了一两个APK,就可以在脚本用mount挂在system分区->package_extract_file直接将APK解压到制定的目录。 2、添加一些脚本命令: 例如,现在这个版本是想要减掉上一个版本的一个system/app/下的一个APK,如果我们可以自己给脚本解析器增加一个delete的命令。例如,增加设置命令的接口来给fastboot发送命令,让recovery系统去告诉fastboot去完成一些只能在fastboot中完成的工作。遇到两次需要这样做:(1)当时,有一个工作是要求在recovery中增加重新划分分区的功能,因为这个工作只能在fastboot中完成,所以我就是这样做的。(2)还有一次是恢复出厂设置后,有些fastboot中的env需要重新设置,才能算是真正的恢复出厂,就也是让recovery去告诉fastboot重新设置下env。 3、修改升级时的画面: 这个基本每次都要做的,如果只是使用安卓原本的recovery来做的话,就只要去源码下bootable/recovery/res/images把图片换换,然后修改下位置和一些简单的细节就可以了。不过了解下它的实现也好: Recovery UI 在recovery源代码recovery.cpp中main有 Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->Init(); ui->SetLocale(locale); ui->SetBackground(RecoveryUI::NONE); if(show_text) ui->ShowText(true); 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 (1)首先新建了一个Device类的对象, Device类封装了一些操作,包括UI的操作(2)调用Device类的GetUI()返回一个DefaultUI对象,recovery中涉及到三个UI类,三个类之间为继承关系,分别为DefaultUI、 ScreenRecoveryUI、RecoveryUI(3)调用DefaultUI类的Init(), DefaultUI类没有Init()方法,因此将调用它的父类ScreenRecoveryUI的Init()(4)同理,调用ScreenRecoveryUI类的SetLocale()来标识几个比较特别的区域(5)同理,调用ScreenRecoveryUI类的SetBackground()设置初始状态的背景图(6)显示recovery的主界面,即一个选择菜单graphics.c给出一些接口,这些接口会调用Pixelflinger的源代码给出的接口,以下是部分接口。Pixelflinger库来进行渲染。 附上minui部分接口的说明,供参考 int gr_init(void); /* 初始化图形显示,主要是打开设备、分配内存、初始化一些参数 */ void gr_exit(void); /* 注销图形显示,关闭设备并释放内存 */ int gr_fb_width(void); /* 获取屏幕的宽度 */ int gr_fb_height(void); /* 获取屏幕的高度 */ gr_pixel *gr_fb_data(void); /* 获取显示数据缓存的地址 */ void gr_flip(void); /* 刷新显示内容 */ void gr_fb_blank(bool blank); /* 清屏 */ void gr_color(unsignedcharr, unsignedcharg, unsignedcharb, unsignedchara); /* 设置颜色 */ void gr_fill(intx,inty,intw,inth); /* 填充矩形区域,参数分别代表起始坐标、矩形区域大小 */ int gr_text(intx,inty,constchar*s); /* 显示字符串 */ int gr_measure(constchar*s); /* 获取字符串在默认字库中占用的像素长度 */ void gr_font_size(int*x,int*y); /* 获取当前字库一个字符所占的长宽 */ void gr_blit(gr_surface source,intsx,intsy,intw,inth,intdx,intdy); /* 填充由source指定的图片 */ unsigned int gr_get_width(gr_surface surface); /* 获取图片宽度 */ Unsigned int gr_get_height(gr_surface surface); /* 获取图片高度 */ /* 根据图片创建显示资源数据,name为图片在mk文件指定的相对路径 */ int res_create_surface(constchar* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); /* 释放资源数据 */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 screen_ui.cpp给出了设置的流程,跟踪这个代码可以知道显示的方法,要注意的是,显示文字的界面必须在showtext为TRUE的时候才会显示,所以用此方法来实现进程界面和选择界面的变换。 4、字体修改 字体比较麻烦吧,我记得我修改字体的时候,觉得很麻烦,不清楚有没有比较好的方法。我的方法:先在graphics.c文件中修改字体头文件。然后:(1)在recovery/miniui中有制作头文件的源码mkfont.c,在/recovery/font中有字体图片,需用gimp工具得到mkfont.c编译所要的结构体。注意gimp输出.c文件时,全部选项都不要选。(2)制作的字体文件.h存在的不足是底色和字体色的问题。修改mkfont.c文件让其相反输出即可。(3)在graphics.c文件中的static void gr_init_font(void)//字体函数是对字体的初始化,在这里是判断字体头文件中的字体,根据阈值0x80来选择透明度,源码默认是255,所以无论怎么调色,最后都是黑色(4)对颜色的修改就要先修改第3点所述部分,再在int gr_text(int x, int y, const char *s)增加想要的字体颜色即可,如:gr_color(255,255,255,252); 5、语言 有时,机器给不同国家,recovery也就要求显示不同语言,那就用这个函数:SetLocale, 该函数根据locale判断所用的字体是否属于阿拉伯语系,阿拉伯语的书写习惯是从右到左,如果是阿拉伯语系的话,就设置一个标志,后面根据这个标志决定从右到左显示文字或进度条。 SetLocale的参数locale赋值逻辑是这样的,先从command文件中读取, command文件中设置locale的命令如”–locale=zh_CN“,如果没有传入locale,初始化过程中会尝试从/cache/recovery/last_locale中读取locale, 如果该文件也没有,则locale不会被赋值,就默认用English.这个其实也是在setting中设置的,会设置为env保持在fastboot中。(我用的方案是这样,不清楚是不是安卓原本的) 6、recovery备份分区 7、增加备份与备份恢复 8、恢复最初升级包的两种方案 9、硬件信息与软件信息

优秀的个人博客,低调大师

【iOS 开发】iOS 10.3 如何更换 app 图标

iOS 10.3 开放了更换 app 图标的 API,核心方法是下面这个: func setAlternateIconName(_ alternateIconName: String?, completionHandler: ((Error?) -> Void)? = nil) 这是官方文档,但是你还需要在 info.plist 里面填一些东西才能让它起作用,这部分官方注释内容在这里。 但 info.plist 如何填写这部分读起来还是有些晦涩,一时可能搞不清楚如何操作,下面做个示范。 Assets.xcassets info.plist <key>CFBundleIcons</key> <dict> <key>CFBundleAlternateIcons</key> <dict> <key>blackBgColor</key> <dict> <key>CFBundleIconFiles</key> <array> <string>blackBgColor</string> </array> <key>UIPrerenderedIcon</key> <false/> </dict> </dict> <key>CFBundlePrimaryIcon</key> <dict> <key>CFBundleIconFiles</key> <array> <string>AppIcon60x60</string> </array> </dict> </dict> 如图,<code>Primary Icon</code> 字段写为 <code>AppIcon60x60</code> 是因为这里 xcassets 里面我只导入了 60pt@2x 和 60pt@3x 的图片资源,这里选为 60 是因为对于 iPhone,60pt 的图片资源图标所需最高质量,更低分辨率的版本系统会自动压缩以展示。 <code>blackBgColor</code> 是我的用于替换原生图标的图片资源。文件名需要和 info.plist 中保持一致(注意 info.plist 中用到了两次 "blackBgColor"),同时这也是你在代码中设置图标时,需要给 API 传入的参数。同样是 60pt@2x 和 60pt@3x 的图片资源,文件不通过 Assets.xcassets 添加进来,而是直接放到目录中。 如果你需要支持 iPad,建议这里使用 83.5pt(iPad Pro)的图片资源。另外还有些其他关于在 iPad 上替换图标的注意事项,在这里有说明,注意我们这里在 info.plist 里面所用的 key 是<code> CFBundleIcons</code>,还有另外一个 key 是 <code>CFBundleIcons~ipad</code>。 替换图标部分的代码就超级简单了: import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func changeAppIcon(_ sender: Any) { if UIApplication.shared.supportsAlternateIcons { print("you can change this app's icon") }else { print("you cannot change this app's icon") return } if let name = UIApplication.shared.alternateIconName { // CHANGE TO PRIMARY ICON UIApplication.shared.setAlternateIconName(nil) { (err:Error?) in print("set icon error:\(String(describing: err))") } print("the alternate icon's name is \(name)") }else { // CHANGE TO ALTERNATE ICON UIApplication.shared.setAlternateIconName("blackBgColor") { (err:Error?) in print("set icon error:\(String(describing: err))") } } } } 这是上述所有内容的完整 demo 地址 screentshot

优秀的个人博客,低调大师

Android和Unity混合开发——解决方案

按这篇文章来做 http://blog.csdn.net/a369414641/article/details/53436477 要注意的地方 1.app是Android Libray,否则无法打包出.aar包 你可以自已在工程里加Android Libray,名字无所谓 2.文章中没有说manifest怎么修改lauch activty,只 说拿Unity安装目录下的manifest.xml,本人修改后出错 提示un merge,大意是无法合并的意思,目前不知如何解决 所以本人未修改,然后可以生成APK 收获是虽然启动时不能指定自已的MainActivety,但是代码里用AndroidJavaClass可以调用到.aar包里面的类

优秀的个人博客,低调大师

VR(虚拟现实)开发资源汇总

Daydream https://developers.google.cn Virtual Reality High Performance googlevr Google VR for Android GVR SDK and NDKRelease Notes http://www.apkmirror.com/apk/google-inc com.google.vr.vrcore com.google.android.vr.home Google Inc. Daydream (Daydream) Google Inc. Google VR Services (Daydream) What is Google Daydream DaydreamPerformance HUD Daydream Controller手柄数据的解析 How do I fix my Daydream controller Google Daydream Controller Teardown Daydream controller : Comprehensive guide Use the Daydream View controller and headset 谷歌Daydream VR平台应用需求:如何设计VR应用 Algorithm Conversion Quaternion to Euler Conversion Euler to Quaternion ATW Timewarp Asynchronous timewarp How Does Time Warping Work Difference_between_ATW_ASW_and_Reprojection Bluetooth Bluetooth Core Specification HID-over-GATT NordicSemiconductor Dialog-semiconductor SmartBond™ DA14681 Android Bluetooth Low Energy NordicSDK and Documentation Calculate throughput for a BLE link Introduction to Bluetooth Low Energy Android Lollipop: Bluetooth LE Matures Bluetooth Low Energy vs. Classic Bluetooth Getting Started with Bluetooth Low Energy Maximizing BLE Throughput on iOS and Android How different BLE packet types influence throughput Analysis of Latency Performance of Bluetooth Low Energy (BLE) Networks FTS4BT™ Bluetooth® Protocol Analyzer and Packet Sniffer CPAS-11(Frontline_16.10.12321.12610) Latency Front Buffer Rendering Reducing latency in mobile VR by using single buffered strip rendering The importance of fine-grained GPU preemption support for VR Touch IQS525-B000 Unity Unity Editor and Android Runtime for Daydream Unity Download GVR-Unity-SDK Unity3d Quaternion 四元数(Quaternion)和旋转 Qualcomm 基于骁龙 VR SDK的VR图形优化 EGL EGL10 Tracer for OpenGL ES

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册