摄像头预览 三种方式

摄像头预览有3种方式:

  1. setPreviewDisplay(holder)
  2. setPreviewTexture(surfaceTexture)
  3. 自定义

第一种会给摄像头绑定一个视窗(最终的型态是一个Surface,Egl会对转换成Native的Window与之对应),摄像头采集完数据,再绘制到视窗上,这都是操作系统自发处理的。
第二种是输出到纹理,这个纹理可以做一些处理,加上其他一些OpenGL的操作,最终输出到视窗上。
第三种,也可以使用自定义的方式通过PreviewCallback拿到Frame数据,装换到2D纹理,再输出到视窗。
后两者是可以进行自定义的OpenGL渲染的,比如特效,滤镜等。

Android 推流–camera后置切前置不生效

七牛android端推流的摄像头id (CAMERA_FACING_ID) 目前一共有三种模式:

CAMERA_FACING_ID.CAMERA_FACING_FRONT ==> 前置摄像头

CAMERA_FACING_ID.CAMERA_FACING_BACK ==> 后置摄像头

CAMERA_FACING_ID.CAMERA_FACING_3RD ==> 后置副摄像头,即双摄像头的副摄像头

目前开发者会遇到某些机型,后置摄像头切换之前不成功的情况,这一类问题确认下来是

该机型只有后置摄像头(需要前置的话,直接翻转摄像头)
eg. 华为荣耀7i
所以可以针对这类机型,在调用camera切换的API的时候,给予用户一个提示信息即可。

Android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法

2020-09-08 18:29:53.519 18200-18200/com.shijiusui.p.screenlive I/Glide: Root cause (1 of 1)
     java.io.IOException: Cleartext HTTP traffic to images.cdn.xxxxxx.com not permitted
         at com.android.okhttp.HttpHandler$CleartextURLFilter.checkURLPermitted(HttpHandler.java:115)
         at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:458)
         at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
         at com.bumptech.glide.load.data.HttpUrlFetcher.loadDataWithRedirects(HttpUrlFetcher.java:104)
         at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:59)
         at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
         at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:164)
         at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:154)
         at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:65)
         at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
         at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
         at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:309)
         at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279)
         at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
         at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
         at java.lang.Thread.run(Thread.java:784)

前言:为保证用户数据和设备的安全,Google针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议,而 Android Nougat 和 Oreo 则不受影响。

因此在Android P 使用HttpUrlConnection进行http请求会出现以下异常

W/System.err: java.io.IOException: Cleartext HTTP traffic to **** not permitted

使用OKHttp请求则出现

java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security policy

在Android P系统的设备上,如果应用使用的是非加密的明文流量的http网络请求,则会导致该应用无法进行网络请求,https则不会受影响,同样地,如果应用嵌套了webview,webview也只能使用https请求。

针对这个问题,有以下三种解决方法:

(1)APP改用https请求

(2)targetSdkVersion 降到27以下

(3)更改网络安全配置

前面两个方法容易理解和实现,具体说说第三种方法,更改网络安全配置。

1.在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <network-security-config>
  3. <base-config cleartextTrafficPermitted=”true” />
  4. </network-security-config>

2.接着,在AndroidManifest.xml文件下的application标签增加以下属性:

  1. <application
  2. android:networkSecurityConfig=”@xml/network_security_config”
  3. />

完成,这个时候App就可以访问网络了。

方法四:在AndroidManifest.xml配置文件的<application>标签中直接插入

android:usesCleartextTraffic=”true”

error=86, Bad CPU type in executable

最近在维护一个N久的项目时,发现在mac升级系统为10.15.5后(Android Studio 4.0,gradle 2.3.1),编译失败了,报错如下:

Cannot run program “/Users/xxxx/Android/sdk/build-tools/23.0.1/aapt”: error=86, Bad CPU type in executable

原因是最新版本的macOS Catalina(10.15.5)已经不支持32位的应用了,只能运行64位的应用。

解决方法:升级工程的buildToolsVersion,本例中将23.0.1 升级成25.0.3

leakcanary内存泄露检测工具报错 Dumping memory, app will freeze. Brrr

报错信息:Dumping memory, app will freeze. Brrr

点击查看leaks小黄标,看到报错信息:
java.lang.UnsupportedOperationException: Could not find char array in java.lang.String@334750520 (0x13f3e338)
at com.squareup.leakcanary.HahaHelper.asString(HahaHelper.java:108)
at com.squareup.leakcanary.HeapAnalyzer.findLeakingReference(HeapAnalyzer.java:161)
at com.squareup.leakcanary.HeapAnalyzer.checkForLeak(HeapAnalyzer.java:115)
at com.squareup.leakcanary.internal.HeapAnalyzerService.onHandleIntent(HeapAnalyzerService.java:58)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:76)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.os.HandlerThread.run(HandlerThread.java:65)

查看我本地的集成信息:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

原因是我leakcanary集成版本过低,推荐使用1.5.4或者1.6版本

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6'

修改以后重新运行,一切OK,能成功检测到内存泄漏。

SSH上传本地文件到linux服务器

在linux下一般用scp这个命令来通过ssh传输文件。

1、从服务器上下载文件

scp username@servername:/path/filename /var/www/local_dir(本地目录)

 例如scp root@192.168.0.101:/var/www/test.txt  #把192.168.0.101上的/var/www/test.txt 的文件下载到/var/www/local_dir(本地目录)

2、上传本地文件到服务器

scp /path/filename username@servername:/path

例如scp /var/www/test.php root@192.168.0.101:/var/www/  #把本机/var/www/目录下的test.php文件上传到192.168.0.101这台服务器上的/var/www/目录中

3、从服务器下载整个目录

scp -r username@servername:/var/www/remote_dir/(远程目录) /var/www/local_dir(本地目录)

例如:scp -r root@192.168.0.101:/var/www/test  /var/www/  

4、上传目录到服务器

scp  -r local_dir username@servername:remote_dir

例如:scp -r test  root@192.168.0.101:/var/www/   #把当前目录下的test目录上传到服务器的/var/www/ 目录

注:目标服务器要开启写入权限。

配置 Docker + gitlab-runner 实现线上自动编译

Last login: Mon Apr 20 15:44:24 on ttys002
xxxs-MacBook-Pro:~ kenny$ ssh -l kenny 10.211.55.3
kenny@10.211.55.3's password: 
Last login: Sun Apr 26 14:41:31 2020
[kenny@centos-linux ~]$ ls
android  Desktop  Documents  Downloads  fontconfig  gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@centos-linux ~]$ uname -r
3.10.0-1062.el7.x86_64
[kenny@centos-linux ~]$ sudo yum update
[sudo] kenny 的密码:
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirror.shastacoe.net
 * extras: mirror.teklinks.com
 * updates: ftp.usf.edu
.
.
.

完毕!
[kenny@centos-linux ~]$ sudo yum remove docker  docker-common docker-selinux docker-engin
已加载插件:fastestmirror, langpacks
参数 docker 没有匹配
参数 docker-common 没有匹配
参数 docker-selinux 没有匹配
参数 docker-engin 没有匹配
不删除任何软件包
[kenny@centos-linux ~]$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirror.fileplanet.com
 * extras: mirrors.xmission.com
 * updates: centos.mirror.constant.com
软件包 yum-utils-1.1.31-52.el7.noarch 已安装并且是最新版本
软件包 device-mapper-persistent-data-0.8.5-1.el7.x86_64 已安装并且是最新版本
软件包 7:lvm2-2.02.185-2.el7_7.2.x86_64 已安装并且是最新版本
无须任何处理
[kenny@centos-linux ~]$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
已加载插件:fastestmirror, langpacks
adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
grabbing file https://download.docker.com/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo
repo saved to /etc/yum.repos.d/docker-ce.repo
[kenny@centos-linux ~]$ yum list docker-ce --showduplicates | sort -r
已加载插件:fastestmirror, langpacks
可安装的软件包
 * updates: mirrors.ocf.berkeley.edu
 * extras: mirrors.xmission.com
docker-ce.x86_64            3:19.03.8-3.el7                     docker-ce-stable
docker-ce.x86_64            3:19.03.7-3.el7                     docker-ce-stable
docker-ce.x86_64            3:19.03.6-3.el7                     docker-ce-stable
.
.
.
docker-ce.x86_64            17.03.0.ce-1.el7.centos             docker-ce-stable
Determining fastest mirrors
 * base: mirror.keystealth.org
[kenny@centos-linux ~]$ sudo yum install docker-ce-18.03.1.ce
已加载插件:fastestmirror, langpacks
.
.
.                                                                                                                                                                                                                                                                                                                            

作为依赖被安装:
  container-selinux.noarch 2:2.107-3.el7                                                                                                                                                  pigz.x86_64 0:2.3.3-1.el7.centos                                                                                                                                                 

完毕!
[kenny@centos-linux ~]$ sudo systemctl start docker
[kenny@centos-linux ~]$ sudo systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
[kenny@centos-linux ~]$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  6904  100  6904    0     0   6197      0  0:00:01  0:00:01 --:--:--  6203
Detected operating system as centos/7.
Checking for curl...
Detected curl...
Downloading repository file: https://packages.gitlab.com/install/repositories/runner/gitlab-runner/config_file.repo?os=centos&dist=7&source=script
done.
Installing pygpgme to verify GPG signatures...
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: repos.lax.quadranet.com
 * extras: mirrors.xmission.com
 * updates: bay.uchicago.edu
runner_gitlab-runner-source/signature                                                                                                                                                                                                                                                                                                                |  862 B  00:00:00     
从 https://packages.gitlab.com/runner/gitlab-runner/gpgkey 检索密钥
导入 GPG key 0x51312F3F:
 用户ID     : "GitLab B.V. (package repository signing key) "
 指纹       : f640 3f65 44a3 8863 daa0 b6e0 3f01 618a 5131 2f3f
 来自       : https://packages.gitlab.com/runner/gitlab-runner/gpgkey
从 https://packages.gitlab.com/runner/gitlab-runner/gpgkey/runner-gitlab-runner-366915F31B487241.pub.gpg 检索密钥
runner_gitlab-runner-source/signature                                                                                                                                                                                                                                                                                                                |  951 B  00:00:00 !!! 
runner_gitlab-runner-source/primary                                                                                                                                                                                                                                                                                                                  |  175 B  00:00:02     
软件包 pygpgme-0.3-9.el7.x86_64 已安装并且是最新版本
无须任何处理
Installing yum-utils...
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirror.centos.lax1.serverforge.org
 * extras: mirrors.xmission.com
 * updates: mirrors.ocf.berkeley.edu
软件包 yum-utils-1.1.31-52.el7.noarch 已安装并且是最新版本
无须任何处理
Generating yum cache for runner_gitlab-runner...
导入 GPG key 0x51312F3F:
 用户ID     : "GitLab B.V. (package repository signing key) "
 指纹       : f640 3f65 44a3 8863 daa0 b6e0 3f01 618a 5131 2f3f
 来自       : https://packages.gitlab.com/runner/gitlab-runner/gpgkey
Generating yum cache for runner_gitlab-runner-source...

The repository is setup! You can now install packages.
[kenny@centos-linux ~]$ sudo yum install gitlab-runner
[sudo] kenny 的密码:
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: repos.lax.quadranet.com
 * extras: mirrors.xmission.com
 * updates: centos.mirror.constant.com
runner_gitlab-runner/x86_64/signature                                                                                                                                                                                                                                                                                                                |  862 B  00:00:00     
runner_gitlab-runner/x86_64/signature                                                                                                                                                                                                                                                                                                                | 1.0 kB  00:00:00 !!! 
runner_gitlab-runner-source/signature                                                                                                                                                                                                                                                                                                                |  862 B  00:00:00     
runner_gitlab-runner-source/signature                                                                                                                                                                                                                                                                                                                |  951 B  00:00:00 !!! 


完毕!
[kenny@centos-linux ~]$ sudo gitlab-runner register
[sudo] kenny 的密码:
Runtime platform                                    arch=amd64 os=linux pid=29719 revision=ce065b93 version=12.10.1
Running in system-mode.                            
                                                   
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://10.211.55.3:8888/
Please enter the gitlab-ci token for this runner:
qusy3u_yhLNZzMBHgz9p
Please enter the gitlab-ci description for this runner:
[centos-linux.shared]: android test runner
Please enter the gitlab-ci tags for this runner (comma separated):
android
Registering runner... succeeded                     runner=qusy3u_y
Please enter the executor: custom, docker-ssh, virtualbox, docker+machine, docker-ssh+machine, docker, parallels, shell, ssh, kubernetes:
docker
Please enter the default Docker image (e.g. ruby:2.6):
jangrewe/gitlab-ci-android
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 
[kenny@centos-linux ~]$ cd /etc/gitlab-runner/
-bash: cd: /etc/gitlab-runner/: 权限不够
[kenny@centos-linux ~]$ su
密码:
[root@centos-linux kenny]# cd /etc/gitlab-runner/
[root@centos-linux gitlab-runner]# ls
config.toml
[root@centos-linux gitlab-runner]# vi config.toml 
[root@centos-linux gitlab-runner]# cd -
/home/kenny
[root@centos-linux kenny]# pwd
/home/kenny
[root@centos-linux kenny]# vi /etc/gitlab-runner/config.toml 
[root@centos-linux kenny]# ls
android  Desktop  Documents  Downloads  fontconfig  gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm  hhhha.txt  Music  Pictures  Public  Templates  Videos
[root@centos-linux kenny]# pwd
/home/kenny
[root@centos-linux kenny]# exit
exit
[kenny@centos-linux ~]$ pwd
/home/kenny
[kenny@centos-linux ~]$ ls
android  Desktop  Documents  Downloads  fontconfig  gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@centos-linux ~]$ mkdir android-cache
[kenny@centos-linux ~]$ cd android-cache/
[kenny@centos-linux android-cache]$ ls
[kenny@centos-linux android-cache]$ cd ..
[kenny@centos-linux ~]$ cd android
[kenny@centos-linux android]$ ls
[kenny@centos-linux android]$ cd ..
[kenny@centos-linux ~]$ cd android-cache/
[kenny@centos-linux android-cache]$ mkdir keystore
[kenny@centos-linux android-cache]$ ls
keystore

https://blog.csdn.net/Captive_Rainbow_/article/details/90407356

Android通过add添加多个Fragment事件透传问题

在通过add添加多个Fragment的过程中,如果新fragment某空白区域对应上一fragment的某个控件,点击该空白区域会响应上一fragment控件点击事件,也就是事件透传过去了,解决该问题最简单的方法即为在fragment的根布局文件中加入android:clickable = true属性即可,或者对根布局设个空的点击事件也行。

Android V1及V2签名原理简析

Android为了保证系统及应用的安全性,在安装APK的时候需要校验包的完整性,同时,对于覆盖安装的场景还要校验新旧是否匹配,这两者都是通过Android签名机制来进行保证的,本文就简单看下Android的签名与校验原理,分一下几个部分分析下:

  • APK签名是什么
  • APK签名如何保证APK信息完整性
  • 如何为APK签名
  • APK签名怎么校验

Android的APK签名是什么

签名是摘要与非对称密钥加密相相结合的产物,摘要就像内容的一个指纹信息,一旦内容被篡改,摘要就会改变,签名是摘要的加密结果,摘要改变,签名也会失效。Android APK签名也是这个道理,如果APK签名跟内容对应不起来,Android系统就认为APK内容被篡改了,从而拒绝安装,以保证系统的安全性。目前Android有三种签名V1、V2(N)、V3(P),本文只看前两种V1跟V2,对于V3的轮密先不考虑。先看下只有V1签名后APK的样式:

再看下只有V2签名的APK包样式:

同时具有V1 V2签名:

可以看到,如果只有V2签名,那么APK包内容几乎是没有改动的,META_INF中不会有新增文件,按Google官方文档:在使用v2签名方案进行签名时,会在APK文件中插入一个APK签名分块,该分块位于zip中央目录部分之前并紧邻该部分。在APK签名分块内,签名和签名者身份信息会存储在APK签名方案v2分块中,保证整个APK文件不可修改,如下图:

而V1签名是通过META-INF中的三个文件保证签名及信息的完整性:

APK签名如何保证APK信息完整性

V1签名是如何保证信息的完整性呢?V1签名主要包含三部分内容,如果狭义上说签名跟公钥的话,仅仅在.rsa文件中,V1签名的三个文件其实是一套机制,不能单单拿一个来说事,

MANIFEST.MF:摘要文件,存储文件名与文件SHA1摘要(Base64格式)键值对,格式如下,其主要作用是保证每个文件的完整性

如果对APK中的资源文件进行了替换,那么该资源的摘要必定发生改变,如果没有修改MANIFEST.MF中的信息,那么在安装时候V1校验就会失败,无法安装,不过如果篡改文件的同时,也修改其MANIFEST.MF中的摘要值,那么MANIFEST.MF校验就可以绕过。

CERT.SF:二次摘要文件,存储文件名与MANIFEST.MF摘要条目的SHA1摘要(Base64格式)键值对,格式如下

CERT.SF个人觉得有点像冗余,更像对文件完整性的二次保证,同绕过MANIFEST.MF一样,.SF校验也很容易被绕过。

CERT.RSA 证书(公钥)及签名文件,存储keystore的公钥、发行信息、以及对CERT.SF文件摘要的签名信息(利用keystore的私钥进行加密过)

CERT.RSA与CERT.SF是相互对应的,两者名字前缀必须一致,不知道算不算一个无聊的标准。看下CERT.RSA文件内容:

CERT.RSA文件里面存储了证书公钥、过期日期、发行人、加密算法等信息,根据公钥及加密算法,Android系统就能计算出CERT.SF的摘要信息,其严格的格式如下:

从CERT.RSA中,我们能获的证书的指纹信息,在微信分享、第三方SDK申请的时候经常用到,其实就是公钥+开发者信息的一个签名:

除了CERT.RSA文件,其余两个签名文件其实跟keystore没什么关系,主要是文件自身的摘要及二次摘要,用不同的keystore进行签名,生成的MANIFEST.MF与CERT.SF都是一样的,不同的只有CERT.RSA签名文件。也就是说前两者主要保证各个文件的完整性,CERT.RSA从整体上保证APK的来源及完整性,不过META_INF中的文件不在校验范围中,这也是V1的一个缺点。V2签名又是如何保证信息的完整性呢?

V2签名块如何保证APK的完整性

前面说过V1签名中文件的完整性很容易被绕过,可以理解单个文件完整性校验的意义并不是很大,安装的时候反而耗时,不如采用更加简单的便捷的校验方式。V2签名就不针对单个文件校验了,而是针对APK进行校验,将APK分成1M的块,对每个块计算值摘要,之后针对所有摘要进行摘要,再利用摘要进行签名。

也就是说,V2摘要签名分两级,第一级是对APK文件的1、3 、4 部分进行摘要,第二级是对第一级的摘要集合进行摘要,然后利用秘钥进行签名。安装的时候,块摘要可以并行处理,这样可以提高校验速度。

简单的APK签名流程(签名原理)

APK是先摘要,再签名,先看下摘要的定义:Message Digest:摘要是对消息数据执行一个单向Hash,从而生成一个固定长度的Hash值,这个值就是消息摘要,至于常听到的MD5、SHA1都是摘要算法的一种。理论上说,摘要一定会有碰撞,但只要保证有限长度内碰撞率很低就可以,这样就能利用摘要来保证消息的完整性,只要消息被篡改,摘要一定会发生改变。但是,如果消息跟摘要同时被修改,那就无从得知了。

而数字签名是什么呢(公钥数字签名),利用非对称加密技术,通过私钥对摘要进行加密,产生一个字符串,这个字符串+公钥证书就可以看做消息的数字签名,如RSA就是常用的非对称加密算法。在没有私钥的前提下,非对称加密算法能确保别人无法伪造签名,因此数字签名也是对发送者信息真实性的一个有效证明。不过由于Android的keystore证书是自签名的,没有第三方权威机构认证,用户可以自行生成keystore,Android签名方案无法保证APK不被二次签名。

知道了摘要跟签名的概念后,再来看看Android的签名文件怎么来的?如何影响原来APK包?通过sdk中的apksign来对一个APK进行签名的命令如下:

 ./apksigner sign  --ks   keystore.jks  --ks-key-alias keystore  --ks-pass pass:XXX  --key-pass pass:XXX  --out output.apk input.apk

其主要实现在 android/platform/tools/apksig 文件夹中,主体是ApkSigner.java的sign函数,函数比较长,分几步分析

private void sign(
        DataSource inputApk,
        DataSink outputApkOut,
        DataSource outputApkIn)
                throws IOException, ApkFormatException, NoSuchAlgorithmException,
                        InvalidKeyException, SignatureException {
    // Step 1. Find input APK's main ZIP sections
    ApkUtils.ZipSections inputZipSections;
    <!--根据zip包的结构,找到APK中包内容Object-->
    try {
        inputZipSections = ApkUtils.findZipSections(inputApk);
    ...

先来看这一步,ApkUtils.findZipSections,这个函数主要是解析APK文件,获得ZIP格式的一些简单信息,并返回一个ZipSections,

 public static ZipSections findZipSections(DataSource apk)
            throws IOException, ZipFormatException {
        Pair<ByteBuffer, Long> eocdAndOffsetInFile =
                ZipUtils.findZipEndOfCentralDirectoryRecord(apk);
        ByteBuffer eocdBuf = eocdAndOffsetInFile.getFirst();
        long eocdOffset = eocdAndOffsetInFile.getSecond();
        eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
        long cdStartOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocdBuf);
        ...
        long cdSizeBytes = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocdBuf);
        long cdEndOffset = cdStartOffset + cdSizeBytes;
        int cdRecordCount = ZipUtils.getZipEocdCentralDirectoryTotalRecordCount(eocdBuf);
        return new ZipSections(
                cdStartOffset,
                cdSizeBytes,
                cdRecordCount,
                eocdOffset,
                eocdBuf);
    }

ZipSections包含了ZIP文件格式的一些信息,比如中央目录信息、中央目录结尾信息等,对比到zip文件格式如下:

获取到 ZipSections之后,就可以进一步解析APK这个ZIP包,继续走后面的签名流程,

    long inputApkSigningBlockOffset = -1;
    DataSource inputApkSigningBlock = null;
    <!--检查V2签名是否存在-->
    try {
        Pair<DataSource, Long> apkSigningBlockAndOffset =
                V2SchemeVerifier.findApkSigningBlock(inputApk, inputZipSections);
        inputApkSigningBlock = apkSigningBlockAndOffset.getFirst();
        inputApkSigningBlockOffset = apkSigningBlockAndOffset.getSecond();
    } catch (V2SchemeVerifier.SignatureNotFoundException e) {
    <!--V2签名不存在也没什么问题,非必须-->
}
 <!--获取V2签名以外的信息区域-->
 DataSource inputApkLfhSection =
            inputApk.slice(
                    0,
                    (inputApkSigningBlockOffset != -1)
                            ? inputApkSigningBlockOffset
                            : inputZipSections.getZipCentralDirectoryOffset());

可以看到先进行了一个V2签名的检验,这里是用来签名,为什么先检验了一次?第一次签名的时候会直接走这个异常逻辑分支,重复签名的时候才能获到取之前的V2签名,怀疑这里获取V2签名的目的应该是为了排除V2签名,并获取V2签名以外的数据块,因为签名本身不能被算入到签名中,之后会解析中央目录区,构建一个DefaultApkSignerEngine用于签名

      <!--解析中央目录区,目的是为了解析AndroidManifest-->
    // Step 2. Parse the input APK's ZIP Central Directory
    ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections);
    List<CentralDirectoryRecord> inputCdRecords =
            parseZipCentralDirectory(inputCd, inputZipSections);

    // Step 3. Obtain a signer engine instance
    ApkSignerEngine signerEngine;
    if (mSignerEngine != null) {
        signerEngine = mSignerEngine;
    } else {
        // Construct a signer engine from the provided parameters
        ...
        List<DefaultApkSignerEngine.SignerConfig> engineSignerConfigs =
                new ArrayList<>(mSignerConfigs.size());
        <!--一般就一个-->
        for (SignerConfig signerConfig : mSignerConfigs) {
            engineSignerConfigs.add(
                    new DefaultApkSignerEngine.SignerConfig.Builder(
                            signerConfig.getName(),
                            signerConfig.getPrivateKey(),
                            signerConfig.getCertificates())
                            .build());
        }
        <!--默认V1 V2都启用-->
        DefaultApkSignerEngine.Builder signerEngineBuilder =
                new DefaultApkSignerEngine.Builder(engineSignerConfigs, minSdkVersion)
                        .setV1SigningEnabled(mV1SigningEnabled)
                        .setV2SigningEnabled(mV2SigningEnabled)
                        .setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved);
        if (mCreatedBy != null) {
            signerEngineBuilder.setCreatedBy(mCreatedBy);
        }
        signerEngine = signerEngineBuilder.build();
    }

先解析中央目录区,获取AndroidManifest文件,获取minSdkVersion(影响签名算法),并构建DefaultApkSignerEngine,默认情况下V1 V2签名都是打开的。

    // Step 4. Provide the signer engine with the input APK's APK Signing Block (if any)
    <!--忽略这一步-->
    if (inputApkSigningBlock != null) {
        signerEngine.inputApkSigningBlock(inputApkSigningBlock);
    }

    // Step 5. Iterate over input APK's entries and output the Local File Header + data of those
    // entries which need to be output. Entries are iterated in the order in which their Local
    // File Header records are stored in the file. This is to achieve better data locality in
    // case Central Directory entries are in the wrong order.
    List<CentralDirectoryRecord> inputCdRecordsSortedByLfhOffset =
            new ArrayList<>(inputCdRecords);
    Collections.sort(
            inputCdRecordsSortedByLfhOffset,
            CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
    int lastModifiedDateForNewEntries = -1;
    int lastModifiedTimeForNewEntries = -1;
    long inputOffset = 0;
    long outputOffset = 0;
    Map<String, CentralDirectoryRecord> outputCdRecordsByName =
            new HashMap<>(inputCdRecords.size());
    ...

    // Step 6. Sort output APK's Central Directory records in the order in which they should
    // appear in the output
    List<CentralDirectoryRecord> outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10);
    for (CentralDirectoryRecord inputCdRecord : inputCdRecords) {
        String entryName = inputCdRecord.getName();
        CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName);
        if (outputCdRecord != null) {
            outputCdRecords.add(outputCdRecord);
        }
    }

第五步与第六步的主要工作是:apk的预处理,包括目录的一些排序之类的工作,应该是为了更高效处理签名,预处理结束后,就开始签名流程,首先做的是V1签名(默认存在,除非主动关闭):

    // Step 7. Generate and output JAR signatures, if necessary. This may output more Local File
    // Header + data entries and add to the list of output Central Directory records.
    ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest =
            signerEngine.outputJarEntries();
    if (outputJarSignatureRequest != null) {
        if (lastModifiedDateForNewEntries == -1) {
            lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS)
            lastModifiedTimeForNewEntries = 0;
        }
        for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry :
                outputJarSignatureRequest.getAdditionalJarEntries()) {
            String entryName = entry.getName();
            byte[] uncompressedData = entry.getData();
            ZipUtils.DeflateResult deflateResult =
                    ZipUtils.deflate(ByteBuffer.wrap(uncompressedData));
            byte[] compressedData = deflateResult.output;
            long uncompressedDataCrc32 = deflateResult.inputCrc32;

            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
                    signerEngine.outputJarEntry(entryName);
            if (inspectEntryRequest != null) {
                inspectEntryRequest.getDataSink().consume(
                        uncompressedData, 0, uncompressedData.length);
                inspectEntryRequest.done();
            }

            long localFileHeaderOffset = outputOffset;
            outputOffset +=
                    LocalFileRecord.outputRecordWithDeflateCompressedData(
                            entryName,
                            lastModifiedTimeForNewEntries,
                            lastModifiedDateForNewEntries,
                            compressedData,
                            uncompressedDataCrc32,
                            uncompressedData.length,
                            outputApkOut);


            outputCdRecords.add(
                    CentralDirectoryRecord.createWithDeflateCompressedData(
                            entryName,
                            lastModifiedTimeForNewEntries,
                            lastModifiedDateForNewEntries,
                            uncompressedDataCrc32,
                            compressedData.length,
                            uncompressedData.length,
                            localFileHeaderOffset));
        }
        outputJarSignatureRequest.done();
    }

    // Step 8. Construct output ZIP Central Directory in an in-memory buffer
    long outputCentralDirSizeBytes = 0;
    for (CentralDirectoryRecord record : outputCdRecords) {
        outputCentralDirSizeBytes += record.getSize();
    }
    if (outputCentralDirSizeBytes > Integer.MAX_VALUE) {
        throw new IOException(
                "Output ZIP Central Directory too large: " + outputCentralDirSizeBytes
                        + " bytes");
    }
    ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes);
    for (CentralDirectoryRecord record : outputCdRecords) {
        record.copyTo(outputCentralDir);
    }
    outputCentralDir.flip();
    DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir);
    long outputCentralDirStartOffset = outputOffset;
    int outputCentralDirRecordCount = outputCdRecords.size();

    // Step 9. Construct output ZIP End of Central Directory record in an in-memory buffer
    ByteBuffer outputEocd =
            EocdRecord.createWithModifiedCentralDirectoryInfo(
                    inputZipSections.getZipEndOfCentralDirectory(),
                    outputCentralDirRecordCount,
                    outputCentralDirDataSource.size(),
                    outputCentralDirStartOffset);

步骤7、8、9都可以看做是V1签名的处理逻辑,主要在V1SchemeSigner中处理,其中包括创建META-INFO文件夹下的一些签名文件,更新中央目录、更新中央目录结尾等,流程不复杂,不在赘述,简单流程就是:

这里特殊提一下重复签名的问题:对一个已经V1签名的APK再次V1签名不会有任何问题,原理就是:再次签名的时候,会排除之前的签名文件。

  public static boolean isJarEntryDigestNeededInManifest(String entryName) {
        // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File

        // Entries which represent directories sould not be listed in the manifest.
        if (entryName.endsWith("/")) {
            return false;
        }

        // Entries outside of META-INF must be listed in the manifest.
        if (!entryName.startsWith("META-INF/")) {
            return true;
        }
        // Entries in subdirectories of META-INF must be listed in the manifest.
        if (entryName.indexOf('/', "META-INF/".length()) != -1) {
            return true;
        }

        // Ignored file names (case-insensitive) in META-INF directory:
        //   MANIFEST.MF
        //   *.SF
        //   *.RSA
        //   *.DSA
        //   *.EC
        //   SIG-*
        String fileNameLowerCase =
                entryName.substring("META-INF/".length()).toLowerCase(Locale.US);
        if (("manifest.mf".equals(fileNameLowerCase))
                || (fileNameLowerCase.endsWith(".sf"))
                || (fileNameLowerCase.endsWith(".rsa"))
                || (fileNameLowerCase.endsWith(".dsa"))
                || (fileNameLowerCase.endsWith(".ec"))
                || (fileNameLowerCase.startsWith("sig-"))) {
            return false;
        }
        return true;
    }

可以看到目录、META-INF文件夹下的文件、sf、rsa等结尾的文件都不会被V1签名进行处理,所以这里不用担心多次签名的问题。接下来就是处理V2签名。

    // Step 10. Generate and output APK Signature Scheme v2 signatures, if necessary. This may
    // insert an APK Signing Block just before the output's ZIP Central Directory
    ApkSignerEngine.OutputApkSigningBlockRequest outputApkSigingBlockRequest =
            signerEngine.outputZipSections(
                    outputApkIn,
                    outputCentralDirDataSource,
                    DataSources.asDataSource(outputEocd));
    if (outputApkSigingBlockRequest != null) {
        byte[] outputApkSigningBlock = outputApkSigingBlockRequest.getApkSigningBlock();
        outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length);
        ZipUtils.setZipEocdCentralDirectoryOffset(
                outputEocd, outputCentralDirStartOffset + outputApkSigningBlock.length);
        outputApkSigingBlockRequest.done();
    }

    // Step 11. Output ZIP Central Directory and ZIP End of Central Directory
    outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut);
    outputApkOut.consume(outputEocd);
    signerEngine.outputDone();
}

V2SchemeSigner处理V2签名,逻辑比较清晰,直接对V1签名过的APK进行分块摘要,再集合签名,V2签名不会改变之前V1签名后的任何信息,签名后,在中央目录前添加V2签名块,并更新中央目录结尾信息,因为V2签名后,中央目录的偏移会再次改变:

APK签名怎么校验

签名校验的过程可以看做签名的逆向,只不过覆盖安装可能还要校验公钥及证书信息一致,否则覆盖安装会失败。签名校验的入口在PackageManagerService的install里,安装官方文档,7.0以上的手机优先检测V2签名,如果V2签名不存在,再校验V1签名,对于7.0以下的手机,不存在V2签名校验机制,只会校验V1,所以,如果你的App的miniSdkVersion<24(N),那么你的签名方式必须内含V1签名:

校验流程就是签名的逆向,了解签名流程即可,本文不求甚解,有兴趣自己去分析,只是额外提下覆盖安装,覆盖安装除了检验APK自己的完整性以外,还要校验证书是否一致只有证书一致(同一个keystore签名),才有可能覆盖升级。覆盖安装同全新安装相比较多了几个校验

  • 包名一致
  • 证书一致
  • versioncode不能降低

这里只关心证书部分:

    // Verify: if target already has an installer package, it must
    // be signed with the same cert as the caller.
    if (targetPackageSetting.installerPackageName != null) {
        PackageSetting setting = mSettings.mPackages.get(
                targetPackageSetting.installerPackageName);
        // If the currently set package isn't valid, then it's always
        // okay to change it.
        if (setting != null) {
            if (compareSignatures(callerSignature,
                    setting.signatures.mSignatures)
                    != PackageManager.SIGNATURE_MATCH) {
                throw new SecurityException(
                        "Caller does not have same cert as old installer package "
                        + targetPackageSetting.installerPackageName);
            }
        }
    }

V1、V2签名下美团多渠道打包的切入点

  • V1签名:META_INFO文件夹下增加文件不会对校验有任何影响,则是美团V1多渠道打包方案的切入点
  • V2签名:V2签名块中可以添加一些附属信息,不会对签名又任何影响,这是V2多渠道打包的切入点。

总结

V1签名靠META_INFO文件夹下的签名文件V2签名依靠中央目录前的V2签名快,ZIP的目录结构不会改变,当然结尾偏移要改。V1 V2签名可以同时存在(miniSdkVersion 7.0以下如果没有V1签名是不可以的)多去到打包的切入点原则:附加信息不影响签名验证

https://www.jianshu.com/p/95096ca209e1

Centos 7 搭建 Gitlab 服务器

Last login: Mon Mar 30 13:22:46 on ttys000
xxxs-MacBook-Pro:~ kenny$ ifconfig
lo0: flags=8049 mtu 16384
	options=1203
	inet 127.0.0.1 netmask 0xff000000 
	inet6 ::1 prefixlen 128 
	。
    。
    。
	options=3
	ether 00:1c:42:00:00:09 
	inet 10.37.129.2 netmask 0xffffff00 broadcast 10.37.129.255
	media: autoselect
	status: active
xxxs-MacBook-Pro:~ kenny$ ifconfig | grep 192
xxxs-MacBook-Pro:~ kenny$ ifconfig | grep 10
gif0: flags=8010 mtu 1280
	media: autoselect (100baseTX )
	inet 10.66.105.48 netmask 0xfffff800 broadcast 10.66.111.255
		maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
	inet 10.211.55.2 netmask 0xffffff00 broadcast 10.211.55.255
	inet 10.37.129.2 netmask 0xffffff00 broadcast 10.37.129.255
xxxs-MacBook-Pro:~ kenny$ ping 10.211.55.3
PING 10.211.55.3 (10.211.55.3): 56 data bytes
64 bytes from 10.211.55.3: icmp_seq=0 ttl=64 time=0.291 ms
64 bytes from 10.211.55.3: icmp_seq=1 ttl=64 time=0.306 ms
64 bytes from 10.211.55.3: icmp_seq=2 ttl=64 time=0.440 ms
^C
--- 10.211.55.3 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.291/0.346/0.440/0.067 ms
xxxs-MacBook-Pro:~ kenny$ ssh -l kenny 10.211.55.3
The authenticity of host '10.211.55.3 (10.211.55.3)' can't be established.
ECDSA key fingerprint is SHA256:3u3go1kEgAKFyUXQf+Bv9MmyCop4dUUgyuDSH0EJw3I.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.211.55.3' (ECDSA) to the list of known hosts.
kenny@10.211.55.3's password: 
Last login: Mon Apr 20 15:42:30 2020
[kenny@localhost ~]$ ls
Desktop  Documents  Downloads  fontconfig  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@localhost ~]$ ls
Desktop  Documents  Downloads  fontconfig  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@localhost ~]$ ls
Desktop  Documents  Downloads  fontconfig  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@localhost ~]$ mkdir android && cd android
[kenny@localhost android]$ wget https://dl.google.com/android/repository/tools_r26.1.1-linux.zip
--2020-04-20 16:06:33--  https://dl.google.com/android/repository/tools_r26.1.1-linux.zip
正在解析主机 dl.google.com (dl.google.com)... 203.208.39.225, 203.208.39.230, 203.208.39.238, ...
正在连接 dl.google.com (dl.google.com)|203.208.39.225|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 404 Not Found
2020-04-20 16:06:34 错误 404:Not Found。

[kenny@localhost android]$ ls
[kenny@localhost android]$ apt -get --quiet update --yes
bash: apt: 未找到命令...
[kenny@localhost android]$ ping www.baidu.com
PING www.a.shifen.com (180.101.49.12) 56(84) bytes of data.
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=1 ttl=128 time=10.8 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=2 ttl=128 time=16.7 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=3 ttl=128 time=17.3 ms
^C
--- www.a.shifen.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 10.877/14.993/17.396/2.924 ms
[kenny@localhost android]$ sudo yum install -y curl policycoreutils-python openssh-server

我们信任您已经从系统管理员那里了解了日常注意事项。
总结起来无外乎这三点:

    #1) 尊重别人的隐私。
    #2) 输入前要先考虑(后果和风险)。
    #3) 权力越大,责任越大。

[sudo] kenny 的密码:
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.njupt.edu.cn
 * updates: mirrors.ustc.edu.cn
软件包 policycoreutils-python-2.5-33.el7.x86_64 已安装并且是最新版本
软件包 openssh-server-7.4p1-21.el7.x86_64 已安装并且是最新版本
正在解决依赖关系
--> 正在检查事务
---> 软件包 curl.x86_64.0.7.29.0-54.el7 将被 升级
---> 软件包 curl.x86_64.0.7.29.0-54.el7_7.2 将被 更新
--> 正在处理依赖关系 libcurl = 7.29.0-54.el7_7.2,它被软件包 curl-7.29.0-54.el7_7.2.x86_64 需要
--> 正在检查事务
---> 软件包 libcurl.x86_64.0.7.29.0-54.el7 将被 升级
---> 软件包 libcurl.x86_64.0.7.29.0-54.el7_7.2 将被 更新
--> 解决依赖关系完成

依赖关系解决

============================================================================================================================================================================================================================================================================================================================================================================
 Package                                                                                架构                                                                                  版本                                                                                             源                                                                                      大小
============================================================================================================================================================================================================================================================================================================================================================================
正在更新:
 curl                                                                                   x86_64                                                                                7.29.0-54.el7_7.2                                                                                updates                                                                                270 k
为依赖而更新:
 libcurl                                                                                x86_64                                                                                7.29.0-54.el7_7.2                                                                                updates                                                                                223 k

事务概要
============================================================================================================================================================================================================================================================================================================================================================================
升级  1 软件包 (+1 依赖软件包)

总计:493 k
Downloading packages:
警告:/var/cache/yum/x86_64/7/updates/packages/curl-7.29.0-54.el7_7.2.x86_64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID f4a80eb5: NOKEY
从 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 检索密钥
导入 GPG key 0xF4A80EB5:
 用户ID     : "CentOS-7 Key (CentOS 7 Official Signing Key) "
 指纹       : 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 软件包     : centos-release-7-7.1908.0.el7.centos.x86_64 (@anaconda)
 来自       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  正在更新    : libcurl-7.29.0-54.el7_7.2.x86_64                                                                                                                                                                                                                                                                                                                        1/4 
  正在更新    : curl-7.29.0-54.el7_7.2.x86_64                                                                                                                                                                                                                                                                                                                           2/4 
  清理        : curl-7.29.0-54.el7.x86_64                                                                                                                                                                                                                                                                                                                               3/4 
  清理        : libcurl-7.29.0-54.el7.x86_64                                                                                                                                                                                                                                                                                                                            4/4 
  验证中      : curl-7.29.0-54.el7_7.2.x86_64                                                                                                                                                                                                                                                                                                                           1/4 
  验证中      : libcurl-7.29.0-54.el7_7.2.x86_64                                                                                                                                                                                                                                                                                                                        2/4 
  验证中      : libcurl-7.29.0-54.el7.x86_64                                                                                                                                                                                                                                                                                                                            3/4 
  验证中      : curl-7.29.0-54.el7.x86_64                                                                                                                                                                                                                                                                                                                               4/4 

更新完毕:
  curl.x86_64 0:7.29.0-54.el7_7.2                                                                                                                                                                                                                                                                                                                                           

作为依赖被升级:
  libcurl.x86_64 0:7.29.0-54.el7_7.2                                                                                                                                                                                                                                                                                                                                        

完毕!
[kenny@localhost android]$ sudo systemctl enable sshd
[kenny@localhost android]$ sudo systemctl start sshd
[kenny@localhost android]$ sudo firewall-cmd --permanent --add-service=http
success
[kenny@localhost android]$ sudo systemctl reload firewalld
[kenny@localhost android]$ cd ..
[kenny@localhost ~]$ ls
android  Desktop  Documents  Downloads  fontconfig  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@localhost ~]$ sudo yum install postfix
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.njupt.edu.cn
 * updates: mirrors.ustc.edu.cn
软件包 2:postfix-2.10.1-7.el7.x86_64 已安装并且是最新版本
无须任何处理
[kenny@localhost ~]$ sudo systemctl enable postfix
[kenny@localhost ~]$ sudo systemctl start postfix
[kenny@localhost ~]$ sudo yum install wget
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.njupt.edu.cn
 * updates: mirrors.ustc.edu.cn
软件包 wget-1.14-18.el7_6.1.x86_64 已安装并且是最新版本
无须任何处理
[kenny@localhost ~]$ wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm
--2020-04-20 16:46:43--  https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm
正在解析主机 mirrors.tuna.tsinghua.edu.cn (mirrors.tuna.tsinghua.edu.cn)... 101.6.8.193
正在连接 mirrors.tuna.tsinghua.edu.cn (mirrors.tuna.tsinghua.edu.cn)|101.6.8.193|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:569226236 (543M) [application/x-redhat-package-manager]
正在保存至: “gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm”

 0% [=>                                              ] 5,070,497    177KB/s 剩余 37m 33s^C                                                                                                                                                                                                                                                                                                                 ] 12,811,545  3.76KB/s 用时 6m 8s  

2020-04-20 17:22:52 (15.2 KB/s) - 在 12811545/569226236 字节处发生读取错误 (Connection reset by peer)。[kenny@localhost ~]$ ls
android  Desktop  Documents  Downloads  fontconfig  gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm  hhhha.txt  Music  Pictures  Public  Templates  Videos
[kenny@localhost ~]$ rpm -i gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm
警告:gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm: 头V4 RSA/SHA1 Signature, 密钥 ID f27eab47: NOKEY
错误:can't create 事务 lock on /var/lib/rpm/.rpm.lock (权限不够)
[kenny@localhost ~]$ sudo rpm -i gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm
[sudo] kenny 的密码:
警告:gitlab-ce-11.9.9-ce.0.el7.x86_64.rpm: 头V4 RSA/SHA1 Signature, 密钥 ID f27eab47: NOKEY
It looks like GitLab has not been configured yet; skipping the upgrade script.

       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.
  


     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/
  

Thank you for installing GitLab!
GitLab was unable to detect a valid hostname for your instance.
Please configure a URL for your GitLab instance by setting `external_url`
configuration in /etc/gitlab/gitlab.rb file.
Then, you can start your GitLab instance by running the following command:
  sudo gitlab-ctl reconfigure

For a comprehensive list of configuration options please see the Omnibus GitLab readme
https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md

[kenny@localhost ~]$ vi /etc/gitlab/gitlab.rb

找到 external_url 这个属性,把地址改为虚拟机 ip 并指定端口号:

[kenny@localhost ~]$ su
密码:
[root@localhost kenny]# cd /etc
[root@localhost etc]# ls
abrt                   auto.smb                   chromium      dbus-1                      environment   gcrypt       grub.d       ipa            latrace.d       logrotate.d               mtools.conf        ntp.conf        pcp.env         pulse       redhat-release    securetty          ssl                 target               virt-who.d
adjtime                avahi                      chrony.conf   dconf                       ethertypes    gdbinit      gshadow      iproute2       ld.so.cache     lsm                       multipath          numad.conf      pinforc         purple      request-key.conf  security           sssd                tcsd.conf            wgetrc
aliases                bash_completion.d          chrony.keys   default                     exports       gdbinit.d    gshadow-     ipsec.conf     ld.so.conf      lvm                       my.cnf             oddjob          pkcs11          python      request-key.d     selinux            statetab            terminfo             wpa_supplicant
aliases.db             bashrc                     cifs-utils    depmod.d                    exports.d     gdm          gss          ipsec.d        ld.so.conf.d    machine-id                my.cnf.d           oddjobd.conf    pki             qemu-ga     resolv.conf       services           statetab.d          tmpfiles.d           X11
alternatives           binfmt.d                   cron.d        dhcp                        favicon.png   geoclue      gssproxy     ipsec.secrets  libaudit.conf   magic                     nanorc             oddjobd.conf.d  plymouth        qemu-kvm    rhsm              sestatus.conf      subgid              trusted-key.key      xdg
anacrontab             bluetooth                  cron.daily    DIR_COLORS                  fcoe          GeoIP.conf   host.conf    iscsi          libblockdev     mailcap                   ndctl              openldap        pm              radvd.conf  rpc               setroubleshoot     subuid              tuned                xinetd.d
asound.conf            brltty                     cron.deny     DIR_COLORS.256color         festival      ghostscript  hostname     issue          libibverbs.d    mail.rc                   netconfig          opt             pnm2ppa.conf    rc0.d       rpm               setuptool.d        sudo.conf           udev                 xml
at.deny                brltty.conf                cron.hourly   DIR_COLORS.lightbgcolor     filesystems   gimp         hosts        issue.net      libnl           makedumpfile.conf.sample  NetworkManager     os-release      polkit-1        rc1.d       rsyncd.conf       sgml               sudoers             udisks2              yum
audisp                 centos-release             cron.monthly  dleyna-server-service.conf  firewalld     gitlab       hosts.allow  java           libpaper.d      man_db.conf               networks           PackageKit      popt.d          rc2.d       rsyslog.conf      shadow             sudoers.d           unbound              yum.conf
audit                  centos-release-upstream    crontab       dnsmasq.conf                flatpak       glvnd        hosts.deny   jvm            libreport       maven                     nfs.conf           pam.d           postfix         rc3.d       rsyslog.d         shadow-            sudo-ldap.conf      updatedb.conf        yum.repos.d
autofs.conf            certmonger                 cron.weekly   dnsmasq.d                   fonts         gnupg        hp           jvm-commmon    libuser.conf    mime.types                nfsmount.conf      papersize       ppp             rc4.d       rwtab             shells             sysconfig           UPower
autofs_ldap_auth.conf  cgconfig.conf              crypttab      dracut.conf                 fprintd.conf  GREP_COLORS  idmapd.conf  kdump.conf     libvirt         mke2fs.conf               nscd.conf          passwd          prelink.conf.d  rc5.d       rwtab.d           skel               sysctl.conf         usb_modeswitch.conf
auto.master            cgconfig.d                 csh.cshrc     dracut.conf.d               fstab         groff        ImageMagick  kernel         locale.conf     modprobe.d                nslcd.conf         passwd-         printcap        rc6.d       samba             smartmontools      sysctl.d            vconsole.conf
auto.master.d          cgrules.conf               csh.login     e2fsck.conf                 fuse.conf     group        init.d       krb5.conf      localtime       modules-load.d            nsswitch.conf      pbm2ppa.conf    profile         rc.d        sane.d            sos.conf           systemd             vimrc
auto.misc              cgsnapshot_blacklist.conf  cups          egl                         fwupd         group-       inittab      krb5.conf.d    login.defs      motd                      nsswitch.conf.bak  pcp             profile.d       rc.local    sasl2             speech-dispatcher  system-release      virc
auto.net               chkconfig.d                cupshelpers   enscript.cfg                gconf         grub2.cfg    inputrc      ksmtuned.conf  logrotate.conf  mtab                      ntp                pcp.conf        protocols       rdma        scl               ssh                system-release-cpe  virt-who.conf
[root@localhost etc]# cd gitlab/
[root@localhost gitlab]# ls
gitlab.rb
[root@localhost gitlab]# vi gitlab.rb 
[root@localhost gitlab]# gitlab-ctl reconfigure
Starting Chef Client, version 13.6.4
resolving cookbooks for run list: ["gitlab"]
Synchronizing Cookbooks:
  - gitlab (0.0.1)
  - package (0.1.0)
  - postgresql (0.1.0)
  - redis (0.1.0)
  - registry (0.1.0)
  - mattermost (0.1.0)
  - consul (0.1.0)
  - gitaly (0.1.0)
  - letsencrypt (0.1.0)
  - nginx (0.1.0)
  - runit (4.3.0)
  - acme (3.1.0)
  - crond (0.1.0)
  - compat_resource (12.19.1)
。
。
。
Recipe: <dynamically defined="" resource="">
  * service[alertmanager] action restart
    - restart service service[alertmanager]
  * service[postgres-exporter] action restart
    - restart service service[postgres-exporter]

Running handlers:
Running handlers complete
Chef Client finished, 475/1268 resources updated in 02 minutes 43 seconds
gitlab Reconfigured!

虽然 gitlab 指定了使用 8888 端口,但是这时候防火墙并没有开放这个端口,所以需要在防火墙配置一下。

[root@localhost gitlab]# firewall-cmd --list-ports

[root@localhost gitlab]# firewal-cmd --zone=public --add-port=8888/tcp --permanent
bash: firewal-cmd: 未找到命令...
[root@localhost gitlab]# firewall-cmd --zone=public --add-port=8888/tcp --permanent
success
[root@localhost gitlab]# firewall-cmd --reload
success
[root@localhost gitlab]# firewall-cmd --list-ports
8888/tcp
[root@localhost gitlab]# packet_write_wait: Connection to 10.211.55.3 port 22: Broken pipe
xxxs-MacBook-Pro:~ kenny$ 
https://blog.csdn.net/Captive_Rainbow_/article/details/90375937

gitlab修改root用户密码

1.使用root权限登录到服务器。
2.使用以下命令启动控制台:
   gitlab-rails console production
3.有多种方法可以查找您的用户。您可以搜索电子邮件或用户名。
   user = User.where(id: 1).first
或者
   user = User.find_by(email: ‘admin@local.host’)
4.现在, 您可以更改密码:
   user.password = ‘secret_pass’
   user.password_confirmation = ‘secret_pass’
5.不要忘记保存:
   user.save!
6.退出控制台, 然后尝试使用新密码登录。

密码 10.211.55.3:8888 root z…4

git中submodule子模块的添加、使用和删除

背景

项目中经常使用别人维护的模块,在git中使用子模块的功能能够大大提高开发效率。
使用子模块后,不必负责子模块的维护,只需要在必要的时候同步更新子模块即可。
本文主要讲解子模块相关的基础命令,详细使用请参考man page。

子模块的添加

添加子模块非常简单,命令如下:
git submodule add <url> <path>

其中,url为子模块的路径,path为该子模块存储的目录路径。
执行成功后,git status会看到项目中修改了.gitmodules,并增加了一个新文件(为刚刚添加的路径)
git diff –cached查看修改内容可以看到增加了子模块,并且新文件下为子模块的提交hash摘要
git commit提交即完成子模块的添加

子模块的使用

克隆项目后,默认子模块目录下无任何内容。需要在项目根目录执行如下命令完成子模块的下载:
git submodule init
git submodule update

或:
git submodule update –init –recursive
执行后,子模块目录下就有了源码,再执行相应的makefile即可。

子模块的更新

子模块的维护者提交了更新后,使用子模块的项目必须手动更新才能包含最新的提交。
在项目中,进入到子模块目录下,执行 git pull更新,查看git log查看相应提交。
完成后返回到项目目录,可以看到子模块有待提交的更新,使用git add,提交即可。

删除子模块

有时子模块的项目维护地址发生了变化,或者需要替换子模块,就需要删除原有的子模块。
删除子模块较复杂,步骤如下:

rm -rf 子模块目录 删除子模块目录及源码
vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config 删除配置项中子模块相关条目
rm .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可
执行完成后,再执行添加子模块命令即可,如果仍然报错,执行如下:

git rm –cached 子模块名称

完成删除后,提交到仓库即可。

RSYNC 的核心算法

原文来自:http://coolshell.cn/articles/7425.html 
 

rsync是unix/linux下同步文件的一个高效算法,它能同步更新两处计算机的文件与目录,并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他大部分类似程序或协定中所未见的重要特性是镜像是只对有变更的部分进行传送。rsync可拷贝/显示目录属性,以及拷贝文件,并可选择性的压缩以及递归拷贝。rsync利用由Andrew Tridgell发明的算法。这里不介绍其使用方法,只介绍其核心算法。我们可以看到,Unix下的东西,一个命令,一个工具都有很多很精妙的东西,怎么学也学不完,这就是Unix的文化啊。

本来不想写这篇文章的,因为原先发现有很多中文blog都说了这个算法,但是看了一下,发现这些中文blog要么翻译国外文章翻译地非常烂,要么就是介绍这个算法介绍得很乱让人看不懂,还有错误,误人不浅,所以让我觉得有必要写篇rsync算法介绍的文章。(当然,我成文比较仓促,可能会有一些错误,请指正)

问题

首先, 我们先来想一下rsync要解决的问题,如果我们要同步的文件只想传不同的部分,我们就需要对两边的文件做diff,但是这两个问题在两台不同的机器上,无法做diff。如果我们做diff,就要把一个文件传到另一台机器上做diff,但这样一来,我们就传了整个文件,这与我们只想传输不同部的初衷相背。

于是我们就要想一个办法,让这两边的文件见不到面,但还能知道它们间有什么不同。这就出现了rsync的算法。

算法

rsync的算法如下:(假设我们同步源文件名为fileSrc,同步目的文件叫fileDst

1)分块Checksum算法。首先,我们会把fileDst的文件平均切分成若干个小块,比如每块512个字节(最后一块会小于这个数),然后对每块计算两个checksum,

  • 一个叫rolling checksum,是弱checksum,32位的checksum,其使用的是Mark Adler发明的adler-32算法,
  • 另一个是强checksum,128位的,以前用md4,现在用md5 hash算法。

为什么要这样?因为若干年前的硬件上跑md4的算法太慢了,所以,我们需要一个快算法来鉴别文件块的不同,但是弱的adler32算法碰撞概率太高了,所以我们还要引入强的checksum算法以保证两文件块是相同的。也就是说,弱的checksum是用来区别不同,而强的是用来确认相同。(checksum的具体公式可以参看这篇文章

2)传输算法。同步目标端会把fileDst的一个checksum列表传给同步源,这个列表里包括了三个东西,rolling checksum(32bits)md5 checksume(128bits)文件块编号

我估计你猜到了同步源机器拿到了这个列表后,会对fileSrc做同样的checksum,然后和fileDst的checksum做对比,这样就知道哪些文件块改变了。

但是,聪明的你一定会有以下两个疑问:

  • 如果我fileSrc这边在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就完全和fileDst这边的不一样了,但理论上来说,我应该只需要传一个字符就好了。这个怎么解决?
  • 如果这个checksum列表特别长,而我的两边的相同的文件块可能并不是一样的顺序,那就需要查找,线性的查找起来应该特别慢吧。这个怎么解决?

很好,让我们来看一下同步源端的算法。

3)checksum查找算法。同步源端拿到fileDst的checksum数组后,会把这个数据存到一个hash table中,用rolling checksum做hash,以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的,所以,hash table的尺寸是2的16次方,对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。(对于hash table,如果你不清楚,建议回去看大学时的数据结构教科书)

顺便说一下,我在网上看到很多文章说,“要对rolling checksum做排序”(比如这篇这篇),这两篇文章都引用并翻译了原作者的这篇文章,但是他们都理解错了,不是排序,就只是把fileDst的checksum数据,按rolling checksum做存到2^16的hash table中,当然会发生碰撞,把碰撞的做成一个链表就好了。这就是原文中所说的第二步——搜索有碰撞的情况。

4)比对算法。这是最关键的算法,细节如下:

4.1)取fileSrc的第一个文件块(我们假设的是512个长度),也就是从fileSrc的第1个字节到第512个字节,取出来后做rolling checksum计算。计算好的值到hash表中查。

4.2)如果查到了,说明发现在fileDst中有潜在相同的文件块,于是就再比较md5的checksum,因为rolling checksume太弱了,可能发生碰撞。于是还要算md5的128bits的checksum,这样一来,我们就有 2^-(32+128) = 2^-160的概率发生碰撞,这太小了可以忽略。如果rolling checksum和md5 checksum都相同,这说明在fileDst中有相同的块,我们需要记下这一块在fileDst下的文件编号

4.3)如果fileSrc的rolling checksum 没有在hash table中找到,那就不用算md5 checksum了。表示这一块中有不同的信息。总之,只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项,那么就会触发算法对fileSrc的rolling动作。于是,算法会住后step 1个字节,取fileSrc中字节2-513的文件块要做checksum,go to (4.1) – 现在你明白什么叫rolling checksum了吧。

4.4)这样,我们就可以找出fileSrc相邻两次匹配中的那些文本字符,这些就是我们要往同步目标端传的文件内容了。

图示

怎么,你没看懂? 好吧,我送佛送上西,画个示意图给你看看(对图中的东西我就不再解释了)。

这样,最终,在同步源这端,我们的rsync算法可能会得到下面这个样子的一个数据数组,图中,红色块表示在目标端已匹配上,不用传输(注:我专门在其中显示了两块chunk #5,相信你会懂的),而白色的地方就是需要传输的内容(注意:这些白色的块是不定长的),这样,同步源这端把这个数组(白色的就是实际内容,红色的就放一个标号)压缩传到目的端,在目的端的rsync会根据这个表重新生成文件,这样,同步完成。

最后想说一下,对于某些压缩文件使用rsync传输可能会传得更多,因为被压缩后的文件可能会非常的不同。对此,对于gzip和bzip2这样的命令,记得开启 “rsyncalbe” 模式。

Updating Homebrew…卡住的解决办法

确保已安装homebrew,更新镜像源

1. 替换brew.git:

cd “$(brew –repo)”
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git

2. 替换homebrew-core.git:

cd “$(brew –repo)/Library/Taps/homebrew/homebrew-core”
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git

3. 重置brew.git:

cd “$(brew –repo)”
git remote set-url origin https://github.com/Homebrew/brew.git

4. 重置homebrew-core.git:

cd “$(brew –repo)/Library/Taps/homebrew/homebrew-core”
git remote set-url origin https://github.com/Homebrew/homebrew-core.git