Working on Chromium / Android / Server
Blog Migration: From Jekyll Chirpy to Hugo PaperMod
Background This blog started in late 2013, hosted on GitHub Pages with Jekyll. After several theme changes, the most recent setup was jekyll-theme-chirpy 7.1.1. After accumulating 55 articles, I decided to reassess the tech stack, looking for something simple, elegant, and developer-oriented. Static Blog Ecosystem in 2026 Before making a choice, I compared the current mainstream static site generators (SSGs): SSG Language Build Speed Highlights Jekyll Ruby Moderate Native GitHub Pages support, most mature ecosystem Hugo Go Extremely fast (<1ms/page) Single binary, zero dependencies Astro JS/TS Fast Zero JS by default, Island Architecture 11ty JS Fast Flexible, multi-template engine Zola Rust Extremely fast Single binary, built-in Sass/highlighting Why Hugo + PaperMod After evaluation, Hugo + PaperMod was the winner: ...
Build AOSP for Pixel 3 XL
The AOSP build process is fairly well understood by now: sync the source code, add device-specific drivers and kernel, and build the target image. I had built AOSP before to debug certain issues, but official support for Pixel 3 XL only goes up to Android 12. Recently, while working on Chromium development, I needed to test WebView on a newer platform, so I used LineageOS 21 for the build instead. ...
A Blind Spot in RSA Encryption with OpenSSL
I thought I had a solid understanding of the differences between RSA encryption schemes and their use cases. But while implementing RSA encryption today, I noticed something puzzling: the ciphertext was different every time, even with the same plaintext and public key. This was a blind spot for me. After researching the internals, here is what I found. The Issue The following code uses OpenSSL’s EVP API for RSA encryption. No random value is explicitly provided, yet the output differs on every call: ...
Jenkins Setup Notes
I was working on an overseas application whose build server was originally located in Shanghai. Due to special circumstances, it needed to be migrated abroad. This post documents the Jenkins migration and configuration process. Prerequisite: Install JDK 11 Using jenv for multi-version Java management: 1 2 3 4 5 6 7 8 9 # Install jenv git clone https://github.com/jenv/jenv.git ~/.jenv echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(jenv init -)"' >> ~/.bashrc source ~/.bashrc # Enable the export plugin and restart the session jenv enable-plugin export exec $SHELL -l Installing Jenkins on CentOS 1 2 3 4 5 6 # Add Jenkins repository and GPG key sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key # Install yum install jenkins Configuring systemd Service 1 2 3 4 sudo vi /etc/systemd/system/jenkins.service sudo systemctl enable /etc/systemd/system/jenkins.service sudo systemctl start jenkins systemctl status jenkins 1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description=jenkins service After=network.target [Service] Type=simple LimitNOFILE=65536 ExecStart=/usr/bin/jenkins User=work Environment="JENKINS_PORT=8082" [Install] WantedBy=multi-user.target Configuring Android SDK 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 wget https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip unzip commandlinetools-linux-8512546_latest.zip chmod a+x cmdline-tools/ cd cmdline-tools/bin # Install required components via sdkmanager ./sdkmanager --sdk_root=/home/work/workspace/android_sdk --list ./sdkmanager --install 'platforms;android-33' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'build-tools;33.0.0' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'cmdline-tools;7.0' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'build-tools;32.0.0' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'build-tools;31.0.0' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'ndk;25.1.8937393' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'platforms;android-28' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'platforms;android-29' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'platforms;android-30' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'platforms;android-31' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'cmake;3.22.1' --sdk_root=/home/work/workspace/android_sdk ./sdkmanager --install 'cmake;3.10.2.4988404' --sdk_root=/home/work/workspace/android_sdk Fixing SSH Key Permission Issues If you encounter Permissions 0664 for '/home/work/.ssh/jenkins_id_rsa' are too open: ...
Configuring and Optimizing Shadowsocks Rust
During the recent holiday, I reorganized the home network with the goal of streaming 4K content without buffering. Since I was rebuilding the server anyway, I decided to migrate from the old setup to the new shadowsocks-rust implementation. System Update 1 sudo apt update && sudo apt upgrade Installing and Configuring SS Option 1: Build from Source via Cargo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Install Rust toolchain curl https://sh.rustup.rs -sSf | sh # Configure Cargo environment (add to .profile / .bash_profile) # CARGO_HOME sets the installation path # target-cpu=native lets rustc optimize for the current CPU CARGO_HOME=/root/cargo RUSTFLAGS="-C target-cpu=native" source .profile # Install build dependencies sudo apt install build-essential # Install shadowsocks-rust cargo install shadowsocks-rust Option 2: Download Prebuilt Binary 1 2 3 4 wget https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.14.3/shadowsocks-v1.14.3.x86_64-unknown-linux-gnu.tar.xz tar -xf shadowsocks-v1.14.3.x86_64-unknown-linux-gnu.tar.xz cp ssserver /usr/local/bin chmod a+x /usr/local/bin/ssserver Configuration 1 2 mkdir /etc/shadowsocks vi /etc/shadowsocks/config.json 1 2 3 4 5 6 { "server": "::", "server_port": 8888, "method": "aes-256-gcm", "password": "pw" } Test the server: ...
Optimizing Android Builds with Self-Hosted Nexus Repository
In Android dependency management, it’s common to configure multiple remote repositories like jcenter, jitpack, google(), and more. Some large projects, such as “Zuiyou”, depend on over 10 repositories. This becomes a significant problem during initial project setup, dependency changes, or network issues — troubleshooting builds becomes a nightmare. I had noticed this issue long ago but kept putting off addressing it. This post documents the Nexus setup process. Installing and Configuring Nexus Prerequisite: JDK 8+. ...
Shadowsocks Setup and Optimization
Network instability at the office was affecting work, so I set up a Shadowsocks server on my VPS for source code pulls. The steps below work on most Linux distributions and have been tested on Ubuntu 16.04 and 18.04. 2024 Update: The Python version of shadowsocks used in this guide is no longer maintained. The actively maintained implementation is shadowsocks-rust, which offers better performance and support for modern encryption protocols. For a fresh setup, refer to the shadowsocks-rust documentation. The original Python-based steps are preserved below for reference. ...
Android & Linux Tips Collection #3
Three quick tips: handling the back button in DialogFragment, chmod permission reference, and the underscore problem in SSL hostnames. DialogFragment Back Button Handling DialogFragment has no direct override for the back button. Two approaches were commonly used. Approach 1: Override in onCreateDialog 1 2 3 4 5 6 7 8 9 @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new Dialog(getActivity(), getTheme()){ @Override public void onBackPressed() { // Handle back press here } }; } Approach 2: Via onKeyListener 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public void onResume() { super.onResume(); Dialog dialog = getDialog(); if (dialog != null) { dialog.setOnKeyListener(this); } } @Override public void onPause() { super.onPause(); Dialog dialog = getDialog(); if (dialog != null) { dialog.setOnKeyListener(null); } } Modern Approach: OnBackPressedDispatcher (AndroidX) For newer versions, use AndroidX’s OnBackPressedDispatcher. Since Fragment 1.6.1, DialogFragment returns a ComponentDialog by default, which has its own OnBackPressedDispatcher: ...
Android MediaStore: GROUP BY via ContentResolver
When building a photo album feature similar to WeChat, I needed to read photos and videos with multi-folder switching — and it had to be faster than WeChat. After some research, the MediaStore approach proved most suitable. Since I hadn’t used it much before, this post serves as a record. The GROUP BY Hack in ContentResolver ContentResolver.query() does not expose a groupBy parameter (unlike SQLiteQueryBuilder.query()), but you can achieve a similar effect by embedding GROUP BY directly into the selection argument. ...
HTTPS Notes
Before working with HTTPS, I recommend reading Android Training: Security with SSL. Many companies have adopted full-site HTTPS, but not all implementations are correct. This post records some issues I encountered. ...