2021/06/07

如何在 CentOS 7 安裝 Heartbeat

Heartbeat 3.x 版拆分為 heartbeat, cluster-glue, resource-agent 三個部分,必須要分開安裝。

  • 確認 hostname

    #vim /etc/hosts,增加兩行 (Server A 和 Server B 上都要各設定兩行)
    192.168.1.35 kokome1
    192.168.1.36 kokome2
  • 安裝編譯需要的套件,以及 kernel-devel-$(uname -r) kernel-headers-$(uname -r)

yum install -y bzip2 autoconf automake libtool glib2-devel libxml2-devel bzip2-devel libtool-ltdl-devel asciidoc libuuid-devel psmisc
  • 安裝 cluster-glue
wget http://hg.linux-ha.org/glue/archive/0a7add1d9996.tar.bz2

tar jxvf 0a7add1d9996.tar.bz2
cd Reusable-Cluster-Components-glue--0a7add1d9996/
groupadd haclient
useradd -g haclient hacluster
./autogen.sh
./configure --prefix=/usr/local/heartbeat/
make
make install

cd ..
  • 安裝 Resource Agents
wget https://github.com/ClusterLabs/resource-agents/archive/v3.9.6.tar.gz
tar zxvf v3.9.6.tar.gz
cd resource-agents-3.9.6/
./autogen.sh
export CFLAGS="$CFLAGS -I/usr/local/heartbeat/include -L/usr/local/heartbeat/lib"
./configure --prefix=/usr/local/heartbeat/

echo "/usr/local/heartbeat/lib" > /etc/ld.so.conf.d/heartbeat.conf
ldconfig

make
make install

cd ..
  • 安裝 heartbeat
wget http://hg.linux-ha.org/heartbeat-STABLE_3_0/archive/958e11be8686.tar.bz2
tar jxvf 958e11be8686.tar.bz2
cd Heartbeat-3-0-958e11be8686
./bootstrap
export CFLAGS="$CFLAGS -I/usr/local/heartbeat/include -L/usr/local/heartbeat/lib"
./configure --prefix=/usr/local/heartbeat/

vi /usr/local/heartbeat/include/heartbeat/glue_config.h
/*define HA_HBCONF_DIR “/usr/local/heartbeat/etc/ha.d/”*/   (這行用/**/註釋掉)

make
make install

cd ..
  • 複製設定
ln -svf /usr/local/heartbeat/lib64/heartbeat/plugins/RAExec/* /usr/local/heartbeat/lib/heartbeat/plugins/RAExec/
ln -svf /usr/local/heartbeat/lib64/heartbeat/plugins/* /usr/local/heartbeat/lib/heartbeat/plugins/

cp /usr/local/heartbeat/share/doc/heartbeat/ha.cf  /usr/local/heartbeat/etc/ha.d
cp /usr/local/heartbeat/share/doc/heartbeat/authkeys /usr/local/heartbeat/etc/ha.d
cp /usr/local/heartbeat/share/doc/heartbeat/haresources /usr/local/heartbeat/etc/ha.d
  • 修改 authkeys

vim /usr/local/heartbeat/etc/ha.d/authkeys #在最後加上

auth 1
1 crc
chmod 600 /usr/local/heartbeat/etc/ha.d/authkeys
  • 設定 ha.cf

因為這個檔案說明太多,尋找和修改不方便,執行下面把原來的換個名稱,把 ha.cf.old 檔案過濾掉註解,剩下的輸出到 ha.cf 檔案中再修改

mv /usr/local/heartbeat/etc/ha.d/ha.cf /usr/local/heartbeat/etc/ha.d/ha.cf.old
sed -e '/^#/d' -e '/^$/d' /usr/local/heartbeat/etc/ha.d/ha.cf.old > /usr/local/heartbeat/etc/ha.d/ha.cf

然後再去修改 /usr/local/heartbeat/etc/ha.d/ha.cf 內容 (參數說明去看剛剛備份的 /etc/ha.d/ha.cf.old)

vi /usr/local/heartbeat/etc/ha.d/ha.cf

logfile /var/log/ha.log
logfacility local0
keepalive 2
deadtime 30
initdead 120
bcast eth0
udpport 694
auto_failback on
node kokome1
node kokome2
ping 192.168.1.1
respawn hacluster /usr/local/heartbeat/libexec/heartbeat/ipfail
apiauth ipfail gid=haclient uid=hacluster

node 值必須和兩台主機的 uname -n 執行結果相符合 可以用 ping gateway ip 來偵測 logfile 是設定 heartbeat 執行情況的 log

  • 設定 haresources

vim /usr/local/heartbeat/etc/ha.d/haresources,最後加入一行

kokome1 IPaddr::192.168.1.13/24/eth0 tomcat httpd

第1個參數是 HA 的主要電腦的 hostname
第2個參數是虛擬介面 IP
第3個參數服務名稱
  • 把 heartbeat 設定全部複製到 Server B 上
scp -r /usr/local/heartbeat/etc/ha.d/ root@192.168.1.12:/usr/local/heartbeat/etc/ha.d/
  • 啟動 heartbeat

    service heartbeat restart
    
    chkconfig heartbeat on
    chkconfig --add heartbeat
    chkconfig --level 2345 heartbeat on
    chkconfig --list heartbeat
    
  • 如果 ifconfig 沒有看到 Virtual IP 的資訊 就改用以下指令

    ip -o -f inet addr show
  • ssh 免密碼

    yum -y install openssh-clients
    
    kokome1:
    mkdir -p /root/.ssh
    cd /root/.ssh
    ssh-keygen -t dsa
    按enter直到完成
    
    (id_kokome1.pub為自己辨識用的名稱)
    scp id_dsa.pub 192.168.1.12:/root/.ssh/id_kokome1.pub
    
    ssh 192.168.1.12
    cd /root/.ssh/
    cat id_kokome1.pub >> authorized_keys
    完成ssh免密碼
    
    kokome2:
    cd /root/.ssh
    ssh-keygen -t dsa
    按enter直到完成
    
    (id_kokome2.pub為自己辨識用的名稱)
    scp id_dsa.pub 192.168.1.11:/root/.ssh/id_kokome2.pub
    
    ssh 192.168.1.11
    cd /root/.ssh/
    cat id_kokome2.pub >> authorized_keys
    完成ssh免密碼
  • 測試

1) Server A 正常時 用瀏覽器連上 http://192.168.1.11,應該會出現 "I am kokome 1" 用瀏覽器連上 http://192.168.1.12,應該會出現 "連線失敗" 一類訊息 用瀏覽器連上 http://192.168.1.13,應該會出現 "I am kokome 1"

(2) Server A 當掉時 假設 Server A 當了,心跳 (heartbeat) 停了,請執行

service heartbeat stop

或把 Server A 關機

(工作接管可能需要 1,2分鐘)

用瀏覽器連上 http://192.168.1.11,應該會出現 "連線失敗" 一類訊息 用瀏覽器連上 http://192.168.1.12,應該會出現 "I am kokome 2" 用瀏覽器連上 http://192.168.1.13,應該會出現 "I am kokome 2"

(3) Server A 又復原

假設 Server A 復原,心跳 (heartbeat) 復原,請執行

service heartbeat start

(工作接管可能需要 1,2分鐘)

用瀏覽器連上 http://192.168.1.11,應該會出現 "I am kokome 1" 用瀏覽器連上 http://192.168.1.12,應該會出現 "連線失敗" 一類訊息 用瀏覽器連上 http://192.168.1.13,應該會出現 "I am kokome 1"

這樣表示測試成功

  • 發生 error

ERROR: Cannot locate resource script httpd

解決方法

cp /usr/sbin/apachectl /usr/local/heartbeat/etc/ha.d/resource.d/httpd

References

Current Linux-HA Release Downloads

centos7.5部署heartbeat+DRBD+mysql高可用方案

CentOS7數據庫架構之NFS+heartbeat+DRBD(親測,詳解)

Heartbeat 3.0.3 介绍及rpm

2021/05/24

如何偵測 usb device 異動

自 linux kernel 2.6 以後, udev 取代了 devfs 與 hotplug,負責處理 device 偵測與管理。他會動態在 /dev 目錄中 create/remove device node。

例如當我們插上一個 COM 轉 USB 的設備線路時,會產生 /dev/ttyUSB0 這個 device,然後程式就能針對這個 device 進行 COM Port IO 處理,當該設備線路被移除時, /dev/ttyUSB0 這個 device 會被刪除。

udev daemon 就是 systemd-udevd,用來接收 device uevents,並與 kernel 溝通。可以在 /etc/udev/rules.d/ 目錄中,產生 .rules 檔案,用來處理 usb device 異動。

首先要用 udevadm 監控 usb device 異動的 uevents

# udevadm monitor --kernel --property --subsystem-match=usb
monitor will print the received events for:
KERNEL - the kernel uevent

產生 ttyUSB0 時

KERNEL[13411.612407] add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1 (usb)
ACTION=add
BUSNUM=002
DEVNAME=/dev/bus/usb/002/008
DEVNUM=008
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1
DEVTYPE=usb_device
MAJOR=189
MINOR=135
PRODUCT=67b/2303/400
SEQNUM=2450
SUBSYSTEM=usb
TYPE=0/0/0

KERNEL[13411.612646] add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1:1.0
DEVTYPE=usb_interface
INTERFACE=255/0/0
MODALIAS=usb:v067Bp2303d0400dc00dsc00dp00icFFisc00ip00in00
PRODUCT=67b/2303/400
SEQNUM=2451
SUBSYSTEM=usb
TYPE=0/0/0

當 ttyUSB0 被移除時

KERNEL[13367.889477] remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1/2-1.1:1.0
DEVTYPE=usb_interface
INTERFACE=255/0/0
MODALIAS=usb:v067Bp2303d0400dc00dsc00dp00icFFisc00ip00in00
PRODUCT=67b/2303/400
SEQNUM=2448
SUBSYSTEM=usb
TYPE=0/0/0

KERNEL[13367.889520] remove   /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1 (usb)
ACTION=remove
BUSNUM=002
DEVNAME=/dev/bus/usb/002/003
DEVNUM=003
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.1
DEVTYPE=usb_device
MAJOR=189
MINOR=130
PRODUCT=67b/2303/400
SEQNUM=2449
SUBSYSTEM=usb
TYPE=0/0/0

建立 add/remove 的 script

sudo vim /root/bin/usbdevice_added.sh

#!/bin/bash
echo "USB device added   at $(date)" >> /var/log/usbdevice.log


sudo vim /root/bin/usbdevice_removed.sh

#!/bin/bash
echo "USB device removed at $(date)" >> /var/log/usbdevice.log


sudo chmod +x /root/bin/usbdevice_added.sh
sudo chmod +x /root/bin/usbdevice_removed.sh

根據 udevadm monitor 得到的屬性 SUBSYSTEM, ACTION, DEVTYPE,設定對應的 script

vim /etc/udev/rules.d/80-usb.rules

SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device",  RUN+="/root/bin/usbdevice_added.sh"
SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="/root/bin/usbdevice_removed.sh"

reload udevadm

sudo udevadm control --reload
tail -f /var/log/usbdevice.log

References

How to Run a Script When USB Devices Is Attached or Removed Using UDEV

How to Use Udev for Device Detection and Management in Linux

How to run custom scripts upon USB device plug-in?

2021/05/17

如何在 Postman 記錄 Cookie

Postman 是用來測試一些 Server Http API 的工具。但在遇到 Server 需要登入後,才能使用的 http request,這時候需要做一些額外的設定。

我們是使用 Java Server,通常在這樣的 Server,會使用 JSESSIONID 這個 Cookie,用來對應 HTTP Server Session ID

首先做一個 Login 的 Request,然後在 Test 頁籤,填入以下的 code。

var a = pm.cookies.get("JSESSIONID");
pm.globals.set("JSESSIONID", a);

執行該 Login 後,可在 Environment 變數中,看到 JSESSIONID 的值

接下來就可以在需要登入後,才能使用的 API 裡面,增加 JSESSIONID 的 Cookie Value

{{JSESSIONID}}

2021/05/10

Install Suricata in CentOS 7

Suricata 是 IDS (Intrusion Detection System),也是 IPS (Intrusion Prevention System),由 Open Security Foundation (OISF) 開發。

IDS 系統是監控網路,檢查並偵測是否有特殊可疑的活動。 IDS 只有監看與記錄的功能。

IPS 是入侵預防系統,是 IDS 的強化產品,可即時偵測並主動防禦。能夠直接把可疑封包移除,中斷連線,發送 email 示警。

以往 IDS 系統都會提到 Snort,而 Suricata 跟 Snort 的主要差異是性能,Suricata 以 multithread 運作。

安裝 suricata

安裝 library

yum install epel-release
sudo yum -y install gcc libpcap-devel pcre-devel libyaml-devel file-devel \
  zlib-devel jansson-devel nss-devel libcap-ng-devel libnet-devel tar make \
  libnetfilter_queue-devel lua-devel PyYAML libmaxminddb-devel rustc cargo \
  lz4-devel

編譯最新的 6.0.0 stable 版

cd /usr/src

wget https://www.openinfosecfoundation.org/download/suricata-6.0.0.tar.gz

tar -xvzf suricata-6.0.0.tar.gz

cd suricata-6.0.0

./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-nfqueue --enable-lua

make

make install
ldconfig

# make install-conf
# make install-rules
make install-full

查詢版本

# suricata -V
This is Suricata version 6.0.0 RELEASE

注意 suricata.yaml 的設定

HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"

直接測試(先以 ifconfig 查詢網路卡的名稱 enp2s0)

# suricata -c /etc/suricata//suricata.yaml -i enp2s0

設定自動啟動

vim /etc/init.d/suricatad

#!/bin/sh
# $Id$
#
# suricatad         Start/Stop the suricata IDS daemon.
#
# chkconfig: 2345 40 60
# description:  Suricata is a lightweight network intrusion detection tool that \
#                currently detects more than 1100 host and network \
#                vulnerabilities, portscans, backdoors, and more.
#

# Source function library.
. /etc/rc.d/init.d/functions

# See how we were called.
case "$1" in
start)
    echo -n "Starting Suricata: "
    daemon PCAP_FRAMES=max /usr/bin/suricata -D -c /etc/suricata/suricata.yaml -i enp2s0
    ;;
stop)
    echo -n "Stopping Suricata: "
    killproc suricata
    echo
    ;;
restart)
    $0 stop
    $0 start
    ;;
status)
    status suricata
    ;;
*)
    echo "Usage: $0 {start|stop|restart|status|}"
    exit 1
esac

exit 0
chmod +x /etc/init.d/suricatad
chkconfig --add suricatad
service suricatad start
service suricatad status

設定 log rorate

vi /etc/logrotate.d/suricata.logrotate

/var/log/suricata/*.log /var/log/suricata/*.json
{
    rotate 30
    missingok
    nocompress
    create
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/suricata.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

nikto 攻擊測試

在另一台機器,以 nikto 作攻擊測試

Nikto 是 web server scanner 可測試 67000 潛在可能有問題的檔案,檢查 webserver index files 及 HTTP server options。

wget https://github.com/sullo/nikto/archive/master.zip -O nikto.zip
unzip nikto.zip
cd nikto-master/program
chmod +x nikto.pl
./nikto.pl -h 192.168.1.5 -p 80

References

Suricata CentOS Installation

Suricata Basic Setup

suricata rules

CentOS 7安裝IDS/IPS安全監測工具(Snorby+Barnyard2+Suricata)

在 CentOS 7.x 上安裝 Suricata 入侵偵測系統

[研究] Suricata 3.0 入侵偵測系統安裝 (CentOS 7.2 x64)

基於CentOS6.5下Suricata(一款高性能的網絡IDS、IPS和網絡安全監控引擎)的搭建(圖文詳解)

2021/05/03

Install ClamAV in CentOS 7

安裝 ClamAV

sudo yum -y install epel-release
sudo yum -y install clamav clamd

# 如果有 selinux,要 enable selinux
sudo setsebool -P antivirus_can_scan_system 1

增加台灣病毒碼更新資料庫

sudo vi /etc/freshclam.conf

DatabaseMirror db.tw.clamav.net
DatabaseMirror clamav.stu.edu.tw

更新病毒碼

sudo freshclam

ClamAV update process started at Wed Oct 28 14:39:25 2020
daily database available for download (remote version: 25970)
Time: 17.0s, ETA: 0.0s [=============================>] 108.67MiB/108.67MiB
Testing database: '/var/lib/clamav/tmp.1a97b/clamav-3ea52eb38408f34bb5f4419b3286235e.tmp-daily.cvd' ...
Database test passed.
daily.cvd updated (version: 25970, sigs: 4336254, f-level: 63, builder: raynman)
main database available for download (remote version: 59)
Time: 17.3s, ETA: 0.0s [=============================>] 112.40MiB/112.40MiB
Testing database: '/var/lib/clamav/tmp.1a97b/clamav-087960cd134405741c214b5dd9915ec8.tmp-main.cvd' ...
Database test passed.
main.cvd updated (version: 59, sigs: 4564902, f-level: 60, builder: sigmgr)
bytecode database available for download (remote version: 331)
Time: 0.4s, ETA: 0.0s [=============================>] 289.44KiB/289.44KiB
Testing database: '/var/lib/clamav/tmp.1a97b/clamav-4905f2da7d8e4b680242ba674f8d9ced.tmp-bytecode.cvd' ...
Database test passed.
bytecode.cvd updated (version: 331, sigs: 94, f-level: 63, builder: anvilleg)

設定自動更新病毒碼

mkdir -p /var/log/clamav
chown -R clamupdate:clamupdate /var/log/clamav/

sudo vi /etc/freshclam.conf
UpdateLogFile /var/log/clamav/freshclam.log

增加自動更新病毒碼 cronjob

vi /etc/cron.daily/freshclam.sh

#!/bin/sh
/usr/bin/freshclam --quiet -l /var/log/clamav/freshclam.log


chmod 755 /etc/cron.daily/freshclam.sh

另一種設定自動更新的方式,是直接啟動 clamav-freshclam.service

設定 clamd

vi /etc/clamd.d/scan.conf

LocalSocket /var/run/clamd.scan/clamd.sock

# 啟用紀錄
LogFile /var/log/clamd/clamd.scan

# 啟用記錄訊息時間
LogTime yes

# 啟用LocalSocket
LocalSocket /var/run/clamd.scan/clamd.sock

# 啟用ExtendedDetecionInfo
ExtendedDetectionInfo yes

# 啟用PidFile
PidFile /var/run/clamd.scan/clamd.pid

產生 clamd log file

mkdir -p /var/log/clamd/
touch /var/log/clamd/clamd.scan
chown -R clamscan:clamscan /var/log/clamd

啟動 clamd

sudo systemctl enable clamd@scan
sudo systemctl start clamd@scan

設定每日自動掃瞄特定目錄

儲存記錄檔於 /var/log/clamscan_daily.log

mkdir -p /var/log/clamscan

vi /etc/cron.daily/clamscan.sh

#!/bin/sh
echo "************" >> /var/log/clamscan/clamscan_daily.log
date '+%Y-%m-%d %T' >> /var/log/clamscan/clamscan_daily.log
/usr/bin/clamscan -i -r /var/www/html >> /var/log/clamscan/clamscan_daily.log
chmod 755 /etc/cron.daily/clamscan.sh

logrotate

vi /etc/logrotate.d/clamscan.logrotate

/var/log/clamscan/clamscan_daily.log {
   missingok
   rotate 30
   daily
}

修改 clamscan.logrotate

vi /etc/logrotate.d/clamav-update

/var/log/clamav/freshclam.log {
    monthly
    notifempty
    missingok
    postrotate
        systemctl try-restart clamav-freshclam.service
    endscript
}

安裝 LMD

# 安裝 EPEL
yum -y install epel-release
# 安裝 mailx
yum install mailx
wget http://www.rfxn.com/downloads/maldetect-current.tar.gz
tar -zxvf maldetect-current.tar.gz

cd maldetect-1.6.4/
./install.sh

ln -s /usr/local/maldetect/maldet /bin/maldet
hash -r

LMD 預設安裝目錄在 /usr/local/maldetect/ , 裡面的 conf.maldet 就是 LMD 的設定檔, 開啟 LMD 的設定檔

vi /usr/local/maldetect/conf.maldet

# 如果要發 email,要改成 1
email_alert="0"
email_addr="user@domain"

quarantine_hits="1"
quarantine_clean="1"

scan_clamscan="1"

掃描後查看 report

maldet -e list

# read report
maldet –report SCANID

# clean report
maldet --clean SCANID

測試 LMD

mkdir -p /var/www/LMDtest
cd /var/www/LMDtest
wget https://www.eicar.org/download/eicar.com 
wget https://www.eicar.org/download/eicar.com.txt 
wget https://www.eicar.org/download/eicar_com.zip 
wget https://www.eicar.org/download/eicarcom2.zip

chown -R httpd:httpd /var/www/LMDtest

掃描

maldet -a /var/www/LMDtest

看 report

maldet --report 201028-1639.16052

PATH:          /var/www/LMDtest/
TOTAL FILES:   4
TOTAL HITS:    4
TOTAL CLEANED: 0

FILE HIT LIST:
{HEX}EICAR.TEST.3 : /var/www/LMDtest/eicar.com => /usr/local/maldetect/quarantine/eicar.com.575417503
{HEX}EICAR.TEST.3 : /var/www/LMDtest/eicarcom2.zip => /usr/local/maldetect/quarantine/eicarcom2.zip.2371612503
{HEX}EICAR.TEST.3 : /var/www/LMDtest/eicar.com.txt => /usr/local/maldetect/quarantine/eicar.com.txt.17479243
{HEX}EICAR.TEST.3 : /var/www/LMDtest/eicar_com.zip => /usr/local/maldetect/quarantine/eicar_com.zip.8340145
===============================================
Linux Malware Detect v1.6.4 < proj@rfxn.com >

隔離區 /usr/local/maldetect/quarantine/

# 刪除隔離區檔案
rm -rf /usr/local/maldetect/quarantine/*

References

Install and configure LMD and Clam AntiVirus on CentOS 7

How to Install and Use Linux Malware Detect (LMD) with ClamAV as Antivirus Engine

CentOS7 安裝防毒軟體 ClamAV

How to Install ClamAV on CentOS 7: A Step-by-Step Guide

CENTOS 7 安裝ClamAV

2021/04/29

Android Service

Android Service

 

Android Service

簡述

  • Service可以在背景不斷的工作,直到停止或是系統無法提供資源為止。
  • Service 需要透過某Activity 或者其他Context 物件來啟動。
  • Service不需要和 user 互動,所以沒有操作介面。
  • 生命週期與Activity是各自獨立的,Activity就算關閉,Service仍然可以繼續執行。
  • 類似 BroadcastReceiver,需要定義一個繼承 Service 的類別,並覆寫其中的生命週期函數,最後在AndroidManifest.xml中宣告才能使用
  • Service可以同時支援 StartedBind 兩種模式。在這種情況下,Service 需要等到兩種模式都被關閉才會觸發onDestroy()事件。
  • Service 只有第一次被啟動時,會執行onCreate(),若重複啟動則不會執行onCreate()
  • Service的運作優先權相當的高,一般來說除非系統資源耗盡,否則 Android 不會主動關閉一個已被啟動的Service。一旦系統有足夠的資源,被 Android 關閉的Service也會被重新啟動。
  • 兩者都需要在AndroidManifest.xml宣告

Service 生命週期

根據執行方式的不同,啟動Service分為兩種,注意只有黃色區塊不一樣: 圖左- Started 模式;圖右- Bind 模式,並根據不同模式複寫白色框框裡的函式。

Started 模式

啟動此模式的 Service,即便退出 Activity 也不會影響 Service 的運行,且Activity無法調用 Service 的方法。

  • 啟動 Service 的方式

    • Client side (Activity):Context.startService(intent)
  • 關閉 Service 的方式

    • Client side (Activity):Context.stopService()
    • Service inside:stopSelf()
  • onStartCommand() 最後需要回傳一個常數,這些常數定義在 Service 類別中。該回傳值是用在如果這個 Service 被 Android 作業系統終止後的行為:

    • Service.START_STICKY Service 如果被中止的話會自動重啟。用在onStartCommand()方法中不需要依賴Intent傳入的資料就可以執行的時候(重新啟動時重新傳入的Intent會是null)。這也是預設使用super.onStartCommnad()的回傳值。
    • Service.START_NOT_STICKY Servcie 如果被中止的話不重新啟動,用在onStartCommand()方法中所執行的工作需要依賴Intent物件內帶進來的參數。
    • Service.START_REDELIVER_INTENT START_STICKY 差不多,但 Android 會在中止 Service 之前將Intent保留下來,等待重新啟動時再將原本的Intent物件交還給onStartCommand()事件。

 

Bind 模式

啟動此模式的 Service,會伴隨著與調用者(Client)一起存活或是退出,當Activity退出Service的運行也會一起終止。

  • 啟動 Service 的方式

    • Client side (Activity):Context.bindService(intent, mServiceConnection, int flags)。其中第二個參數為ServiceConnection 物件,當bindService()綁定成功後,會呼叫此物件內的 onServiceConnected 函式,此函式會接收到由 Service 內的 onBind() 所丟出來的 IBinder 物件來直接操作 Service 內各個 public 的 method。 (ServiceConnection物件實作方式參考下方連結)
  • 關閉 Service 的方式

    • Service inside: unbindService(mServiceConnection);

 

解析 startService

透過以下範例了解startService運作流程,若需詳細程式碼請參考

測試角色

Service 端

  • TestService

Client 端

  • MainActive

結果

这里写图片描述

 

生命週期流程

这里写图片描述

 

再次分析onStartCommand() 的回傳值

  • START_NOT_STICKY

    • 表示當 Service 運行的 process 被 Android 系統強制殺掉之後,不會重新創建該 Service,當然如果在其被殺掉之後一段時間又調用了 startService,那麼該 Service 又將被實例化。
    • Ex:某個 Service 需要定時從 server 獲取最新數據:通過一個定時器每隔指定的N分鐘讓定時器啟動 Service 去獲取 server 的最新數據。當執行到 ServiceonStartCommand 時,在該方法內再規劃一個N分鐘後的定時器用於再次啟動 Service 並開闢一個新的執行緒去執行網絡操作。假設 Service 在從 server 獲取最新數據的過程中被 Android 系統強制殺掉,Service 不會再重新創建,這也沒關係,因為再過N分鐘定時器就會再次啟動該 Service 並重新獲取數據。
  • START_STICKY

    • 表示 Service 運行的 process 被 Android 系統強制殺掉之後,Android 系統會將該 Service 依然設置為 started 狀態(即運行狀態),但是不再保存 onStartCommand 方法傳入的 intent 對象,然後 Android 系統會嘗試再次重新創建該 Service,並執行 onStartCommand 回調方法,但是 onStartCommand 回調方法的 Intent 參數為 null,也就是 onStartCommand 方法雖然會執行但是獲取不到 intent 信息。
    • Ex:如果 Service 可以在任意時刻運行或結束都沒什麼問題,而且不需要 intent 信息,那麼就可以在 onStartCommand 方法中返回 START_STICKY,比如一個用來播放背景音樂功能的 Service 就適合返回該值
  • START_REDELIVER_INTENT

    • 表示 Service 運行的 process 被 Android 系統強制殺掉之後,與返回 START_STICKY 的情況類似,Android 系統會將再次重新創建該 Service,並執行 onStartCommand 回調方法,但是不同的是,Android 系統會再次將 Service 在被殺掉之前最後一次傳入 onStartCommand 方法中的 Intent 再次保留下來,並再次傳入到重新創建後的 ServiceonStartCommand 方法中,這樣我們就能讀取到 intent 參數。
    • Ex:如果 Service 需要依賴具體的 Intent 才能運行(需要從 Intent 中讀取相關數據信息等),並且在強制銷毀後有必要重新創建運行,那麼這樣的 Service 就適合。

解析 bindService

Client 與 Service 在同一個 App 中

透過以下幾個範例了解 bindService 的運作流程,若需詳細程式碼請參考

測試角色

Service 端

  • TestService

Client 端

  • 兩個 Activity,分別為 Active AActive B
  1. Activity A
  2. Activity B

測試流程

測試一

步驟
  • Activity A

    • click bindService
    • click unbindService
結果

測試二

步驟
  • Activity A

    • click bindService
    • click Finish
結果

測試三

步驟
  • Activity A

    • click bindService
    • click start Activity B
  • Activity B

    • click bindService
    • click unbindService
    • click Finish
  • Activity A

    • click unbindService
結果

生命週期流程

Client 與 Service 在同一個 App 中

參考

淺談 Service v.s. thread

  • 看似都是在背景運作,但實際上ServiceThread 沒有任何關係。

  • Service 是運行於主執行緒(Main Thread)上,故若在此執行太耗時的任務,依然會出現 ANR。

  • Thread 是用於開啟一個子執行緒(Child thread),去執行一些耗時作不會阻塞主執行緒的運行。

  • Activity 很難對 Thread 進行控制:

    • Activity 被銷毀後卻沒有主動停止 thread,就會造成没有辦法可以再重新獲取到之前建立的 thread 的實例。
    • 在某 Activity 中建立的 thread,另一個 Activity 無法對其進行操作。
  • Service 方便取得控制權:

    • Activity 被銷毀,其他的 Activity 都可以與 Service 重新進行關聯,就又能夠獲取到原有 Service 中 Binder 的實例。
    • 使用 Service 來處理後台任務,Activity 可以放心地 finish,完全不需要擔心無法對後台任務進行控制的情況。
  • 如果在Service內部做一些很耗時的任務,可以在Service內部建立一個執行緒來處理。因為Service是跑在主執行緒中,會影響到 UI 操作或是阻擋主執行緒的運行

    • ex:在後台播放多媒體檔案會需要檢查SD、在後台紀錄 gps 資訊的變換等等

Ref.

Service背景執行程式

《Android》『Service』- 背景執行服務的基本用法

Android中startService的使用及Service生命周期

Android中bindService的使用及Service生命周期

Android Service和Thread区别

 

2021/04/26

Acoustic Echo Cancellation (AEC)

回音就是發話端發出的聲音,在經過一陣子以後,自己又聽到自己發出的聲音,通常音量會變比較小一點,但還是能識別出來是自己發出的聲音。

回音通常分為兩種:線路回音、聲學回音。

線路回音是因為傳送語音的實體線路造成的,通常是因為電信局之間是使用四線,而電信局到用戶端是兩芯線,中間會有四線、二線的轉換混合器,如果阻抗不匹配(使用不同型號的電線,或是沒有使用負載線圈),就會導致混合器接收線路的語音訊號,外溢流到發送線路上,因此產生了回音。

聲學回音,就是發話端發出的聲音,傳送到接收端後,接收端以 speaker 播放出來,在麥克風收音時,同時又把 speaker 播放的聲音收錄進去,傳送回到發話端,通常造成這樣的回音的終端,並不會知道自己造成了回音,讓另一端聽到回音。

聲學回音又分為直接回音和間接回音。直接回音是指 speaker 播放出來的聲音未經任何反射直接進入麥克風,這種回音延遲最短。間接回音是指 speaker 播放的聲音經不同的路徑一次或多次反射後進入麥克風所產生的回音集合,有可能是房間的牆壁反射造成的。

回音消除主要包含兩個步驟:線性自適應濾波和非線性處理。

線性自適應濾波就是對 fe=f(fs) 求解,建立遠端回聲的語音模型,進行第一輪回音消除。

非線性處理又分為兩個步驟:殘留回音處理和非線性剪切處理。殘留回音處理進行第二輪回音消除,處理殘留回音;非線性剪切處理就是對衰減量達到閾值的語音信號進行比較激進的剪切處理。

自適應濾波演算法

有兩個系列:LMS 與 RLS

LMS 比較穩定,運算量小,比較容易建置與實作。RLS 執行時收斂速度較快,缺點是運算量大。

LMS: Least Mean Square Algorithm

LMS 是最廣泛使用的濾波器演算法,計算過程只有加法與乘法。做法是讓輸出的誤差訊號的均方值最小化。

LMS 的 adaptation step size 是用來調整加權參數的修正速度,數值太小會讓收斂速度變慢,太大會造成發散。

NLMS: Normalized LMS

重新定義 LMS 中的 𝜇 (adaptation step size),讓 𝜇 會隨輸入訊號的 normalization 而改變,提升收斂的穩定性。

RLS: Recursive Least Squares Algorithm

RLS 演算法目標是將輸出誤差信號平方值總和最小化

open source AEC

目前 open source 的 AEC 演算法有 speex 與 webrtc,兩種都是使用自適應濾波 NLMS。

speex 是使用 MDF 雙濾波結構,用前綠波跟本次濾波的結果來調整濾波器的參數,濾波器的長度很長。

webrtc AEC 比較複雜,包含了 delay 估計, NLMS 及 NLP,舒適噪音。NLMS 的部分只用了 12 block。

目前 speex AEC 用在嵌入式系統,硬體不需要延時模組,速度比較快。webrtc AEC 用在軟體。

References

聲學回聲消除(Acoustic Echo Cancellation)原理與實現

回聲消除基本介紹

音視頻社交中回聲消除技術是如何實現的

AEC 原理

Echo Cancellation

VoIP Echo Cancellation

Understanding the Echo "Phenomenon" Causes and Solutions

可適性濾波器演算法用於聲學回聲消除.pdf

speex AEC

Speex Acoustic Echo Cancellation (AEC) 回聲消除模塊的使用 2013

音訊演算法speex中的aec分析以及解析 2019

speexdsp回聲消除模塊在android中封裝及使用例子 2020

webetc AEC

單獨編譯和使用webrtc音訊回聲消除模組(附完整原始碼+測試音訊檔案)

WebRTC AudioProcessing 3A: AEC AGC ANC 初探

webrtc sample

WebRTC AudioProcessing 3A: AEC AGC ANC 初探

speex aec 與webrtc 回聲消除的比較優化

2021/04/19

Caller ID

Caller ID 來話號碼顯示的功能,是透過交換機做信號傳遞,傳送資料訊息送到使用者的終端設備上(話機),也就是把撥打電話這端的電話號碼,送到接聽電話的那一端。接聽電話端,可以根據電話號碼,選擇要不要接聽電話。

最早提供 Caller ID 服務是由美國及加拿大,Bellcore (Bellcore Client Company) 定義了Type I, Type I, Type III 三種不同類別。

Type Services Supported
Type 1 Calling Number Delivery
Calling Name Delivery
Visual Message Waiting Indicator
Type 2 Type 1 Services +
Calling Identity Delivery on Call Waiting
Type 3 Type 2 Services +
ADSI: both server Display control and Feature Download
ADSI: base visual features such as:
- Visual Screening List Editing
- Calling Waiting Deluxe
- Visual access to information services such as directory, home banking, etc...

ON/OFF Hook

on hook 是電話掛機狀態,off hook 是通話中的狀態。

在 on hook 狀態中,A Party 打電話給 B Party,B Party 能夠顯示來電時間、電話號碼、人名,就是 Caller ID Type I 服務。

在 off hook 狀態中,當 A, B 在通話中,如果 C 打電話給 B,B 能夠在通話中得知 C 的來電時間、電話號碼、人名,B可利用 Call Waiting 決定要不要允許插撥,接聽 C 的來電,這是 Type 2 的服務。

DTMF/FSK

來電號碼顯示目前有兩種信號,一種是簡易的雙音複頻 DTMF: Dual Tone Multi-Frequency,另一種是速度較快,傳輸較複雜的 FSK: Frequency Shift Keying。

DTMF 是由矩陣式 Hi/Lo 頻率編碼組成數字及英文字,只能表示 1, 2, 3, 4, 5, 6, 7, 8, 9, *, 0, #, A, B, C, D 這些字元。所以目前只能提供用來顯示來電號碼而已。

頻率 Hz H1 1209 H2 1336 H3 1477 H4 1633
L1 697 1 2 3 A
L2 770 4 5 6 B
L3 852 7 8 9 C
L4 941 * 0 # D

  

FSK 是用兩個頻率 FH, FL 來代表 0 與 1,接收到訊號後,要解碼比對 ASCII Code 成為數位訊號,故可以處理來電號碼、姓名、時間、語音留言訊息。

DTMF 目前只有瑞典、荷蘭、大陸使用,但已經慢慢改為 FSK。台灣先開通使用 DTMF,因交換機逐漸汰換,逐步開通 FSK,故兩種都有可能。

除了 DTMF, FSK 的差異外,各國傳輸協定是不同的,有的是第一聲鈴響後,傳遞訊號,有的是在極性反轉下傳遞資料,有的是先送自己的號碼,再送對方的號碼,因此 Caller ID 必須考慮不同國家地區使用,才能符合當地的規範。

FSK Bell 202A 與 V.23 規格

Bell 202A 是美國使用,V.23 是英國使用,差異在於頻率跟位準大小

Bell 202A
Link type Simplex
Modulation Phase coherent frequency shift keying
Logical 1 1200 +- 12 Hz
Logical 0 2200 +- 12 Hz
Transmission rate 1200 bite per second
Data Serial, Binary, Asynchronous
Transmission level -13.5 +- 1 dBm into 900 ohm
V.23
Link type Simplex
Modulation FSK
Logical 1 1300 +- 1.5 %
Logical 0 2100 +- 1.5 %
Transmission rate 1200 baud +- 1%
Received signal level for mark: -8 dBV to -40 dBV
Received signal level for space differ by up to 6 dB for mark and space
Unwant signals Total power of extraneous signals in the voice band 300~3400 Hz are at least 20 dB below the signal level
Data Format Serial binary asynchronous ( 1 start bit first, then 8 data bits with least significant bits first, followed by 1 stop bit minimum, up to 10 stop bits maximum) Start bit = 0, Stop bit = 1

美規 Caller ID

有 Type I, II 兩種。

Type I 是 Off Hook 的來電號碼資料,使用 FSK 訊號,資料跟著第一次鈴聲後面進來。

  1. 第一次鈴聲長度約為 0.2~3 seconds

  2. 鈴響後會有一段大約 0.5~1.5 seconds 的靜止狀態

  3. 然後是資料傳輸,會傳入 Caller ID,大約 2.9~3.7 seconds

  4. 資料傳送後,有 >=200ms 的靜止狀態

  5. 然後是第二次鈴聲 1.8 ~ 3 seconds

  6. 然後是第三、第四次 ... 鈴聲

還有一種 Type I 訊號,沒有跟著鈴聲進來,用在 Message Waiting 狀態,也就是有人留言在 Voice Mail 時,交換機會發送訊號過來,通知有人留言。

這一種不跟隨鈴聲的方式,會有一個 OSI (Open Switching Interval) 訊號來讓終端偵測,但根據 Bellcore 規範,這個 OSI 訊號可有可無。 OSI 訊號是一種一段時間,電話線中斷的訊號,由於 OSI 訊號不一定會發生,因此不能當作可偵測的訊號。

  1. OSI 150~350 ms
  2. Silent Interval 300 ms
  3. Data transmission

Type II 除了 Type I 以外,還增加了 OFF Hook,也就是 Call Waiting 狀態,當使用者收到訊息後,可以利用 Flash Key 切換通話對象。

因為沒有鈴聲,所以傳送方式為

CPE Alerting Signal - CPE Sends ACK - FSK Data Transmission - CPE unmutes - OSI - W - SAS X - X1 - CAS Y - t1 - Q - FSK data Z - OSI - S

  1. 一開始會有 0 ~ 300 ms OSI 訊號
  2. 交換機會暫時靜止遠端的聲音,送入 0 ~ 60 ms (W),並送出一個插撥聲音 SAS (X) 250ms ~ 1 sec
  3. 經過 0 ~ 50ms 間斷 X1
  4. 交換機送出 CAS 2130 + 2750 Hz 約 80 ~ 85ms 的複頻 Tone Y
  5. 一般的終端必須要偵測 CAS Tone,用來準備接收 Caller ID 訊號
  6. 終端要先作並機測試,查看是否有並聯的分機佔線,如果沒有,就發出 "D" Tone (941 + 1633 Hz) 給交換機,也就是 ACK 訊號
  7. 交換機會等待 ACK,約等待 160 +- 5ms 的時間 t1
  8. 交換機收到 ACK 後,會等待 50 ~ 500ms 的間隔 Q,然後送出資料 Z
  9. 接著是 OSI 與 間隔 S 0~320ms
  10. 最後回到通話狀態

OSI 不一定會發生

美規 Caller ID 資料格式

分為單筆資料 Single Data 與多筆資料 Multiple Data 兩類,目前大部分是 Multiple Data

SDMF - Single Data Message Format 只能傳送日期時間、電話號碼與 message waiting

MDMF - Multiple Data Message Format 還可以傳送人名,特殊的訊息。在 Call Waiting 的訊號只有 Multiple DAta 的格式。

Single Data 會先出線一連串 250 ms 的 1, 0 交替的捕捉訊號 (Channel Seizure),然後會有 150ms 連續的 1,後面開始才是真正的資料,這些資料包含,訊息種類、長度、訊息字元,然後是校正位元。

訊息種類分為電話號碼及 message waiting 兩種,訊息字元會送出月份、日期、小時、分鐘,及電話號碼,資料送完後,會送出校正字元。

Channel Seizure Signal - Mark Signal - Message Type Word - Mark bits(0~20) - Messge Length Word - Mark bits(0~20) - Message Words - Mark bits(0~20) - Checksum Word

Multiple Data 會在資料種類後面出現多個 Parameter 參數位元組,裡面包含參數種類、參數長度、參數字元。Off Hook Call Waiting 的 Multiple Data 資料開頭沒有 1010... 的捕捉訊號。

台灣 DTMF Caller ID

台灣的 Caller ID 根據 ETSI 修改而來,分為 DTMF, FSK 兩種,目前大部分是用 DTMF。DTMF 用來傳送發話者的電話號碼。

DTMF 台灣規格,在電話機鈴響前,傳送資料,開始碼 Start Bit 是 D,結束 Stop Bit 是 C,在資料傳送狀態下 (NIT: Number Information Transfer State),介於電話線路兩端之間的直流阻抗必須大於 9 M ohm,在資料訊息傳送完成後,終端設備要脫離資料接收狀態,隨電話鈴聲回到靜止狀態,電話號碼顯示的 DTMF 有一定的時序與時間。

Ta 為 DTMF 送訊息結束到第一聲鈴響之間的中段時間,長度約為 150 ~ 700ms。Tb 為傳送結束時,會到靜止狀態的時間,長度要小於等於 150ms。DTMF 資料傳送時間長度要 <= 3 秒。DTMF 每一碼的長度與間隔都會 >= 50ms,在 D 之前會有一個間隔時間 50 ~ 400 ms。

CID 傳送時機 電話號碼 時間日期 發話者名字
DTMF 振鈴前
FSK V.23 極性反轉後振鈴前 SDMF 無, MDMF 有
FSK Bell 202 振鈴中第一個長停歇 SDMF 無, MDMF 有

FSK 用 V.23,但使用 Dual Tone Alerting Signal (DTAS) 取代極性反轉。

DTAS (2130 + 2750 Hz 100±10 ms) → 45 ~ 500 ms → FSK 訊息 → 200 ~ 500 ms → 振鈴

FSK 是採用美規 On Hook 資料傳送格式,但採用 Before Ringer 的傳送方法。

  1. 首先會發送 TAS (Tone Alerting Signal),時間長度 100 +- 10ms,頻率為 2130 Hz + 2750 Hz,誤差 +- 5%。信號位準是每一個音頻 -4 ~ -40 dBm,頻率位準差距最大為 6bB

  2. TAS 後面是靜止 T1,約 45~500ms

  3. T1 結束後,傳送 FSK Data。資料包含日期、時間、電話號碼,因為中文編碼定義問題,目前無法處理姓名

    FSK Data 如下

  1. 後面是一個中斷時間 T2,長度 200 ~ 500ms,然後是振鈴訊號

  2. 從準備接收 FSK Data 到接收結束,起始動作時間長度 T3 <=45ms,結束動作時間長度 T4 <= 150ms。

ADSI

Analog Display Service Interface

ADSI 是 1992 年 12 月 Bellcore Client Company 定義,可用在電話終端與交換機之間的語音與資料傳送,也就是利用 PSTN 進行文字及語音的傳送,進而提供多項加值服務。

這就是 Type III ADSI

ADSI 話機是由 Voice, 警示音 CAS Tone, DTMF, FSK 四種訊號架構的通訊方式

References

FSK CID Format

Caller ID

Caller ID wiki

Caller ID晶片分析 2001年

Caller ID未來市場百家爭鳴 2001年

Frequency-shift keying (FSK) wiki

數位資料與類比訊號 FSK, ASK, PSK

電力線通訊系統

frequency shifting

來電顯示器

Caller ID/SMS

2021/04/12

mongodb 帳號驗證權限

資料庫的權限有四種

  1. readAnyDatabase 任何資料庫的唯讀權限
  2. userAdminAnyDatabase 任何資料庫的讀寫權限
  3. userAdminAnyDatabase 任何資料庫用戶的管理權限
  4. dbAdminAnyDatabase 任何資料庫的管理權限

以往在啟動 mongodb 時,就直接指定 mongod.conf

mongod -f $MONGODB_HOME/conf/mongod.conf

這時候可用 mongo client 連接資料庫

./mongo

查看有沒有 users

db.system.users.find()

查看 user

show users

建立 admin 資料庫的管理者帳號

use admin

db.createUser({
  user : "root",
  pwd : "password",
  roles : [
    "clusterAdmin",
    "dbAdminAnyDatabase",
    "userAdminAnyDatabase",
    "readWriteAnyDatabase"
  ]
})

重新啟動 mognodb (加上 --auth 參數)

mongod --auth -f $MONGODB_HOME/conf/mongod.conf

再次查看 users

> use admin
> db.system.users.find()
Error: error: {
    "ok" : 0,
    "errmsg" : "command find requires authentication",
    "code" : 13,
    "codeName" : "Unauthorized"
}
> show users
2020-09-02T11:36:48.994+0800 E QUERY    [js] Error: command usersInfo requires authentication :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.getUsers@src/mongo/shell/db.js:1763:1
shellHelper.show@src/mongo/shell/utils.js:859:9
shellHelper@src/mongo/shell/utils.js:766:15
@(shellhelp2):1:1

驗證後,就可以查看 users

> use admin
switched to db admin
> db.auth("root", "password")
1
> show users
{
    "_id" : "admin.root",
    "user" : "root",
    "db" : "admin",
    "roles" : [
        {
            "role" : "clusterAdmin",
            "db" : "admin"
        },
        {
            "role" : "dbAdminAnyDatabase",
            "db" : "admin"
        },
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        },
        {
            "role" : "readWriteAnyDatabase",
            "db" : "admin"
        }
    ],
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

針對資料庫,建立管理者

use admin

db.createUser({
  user : "maxkit",
  pwd : "password",
  roles : [
    {role:"readWrite", db:"larzio"}
  ]
})

針對資料庫,建立一般使用者

use larzio

db.createUser({
  user : "maxkit",
  pwd : "max168kit",
  roles : [
    {role:"readWrite", db:"larzio"}
  ]
})

刪除帳號

use admin

db.dropUser("maxkit")

關閉 mognodb

mongo 127.0.0.1:27017 -u root -p 'password' --authenticationDatabase 'admin' --eval "db.getSiblingDB('admin').shutdownServer()"

References

Mongodb 創建管理員帳號與普通帳號