ReDroid (Remote anDroid) 是自架「雲手機」的方案,透過docker在電腦上跑Android系統容器,再利用Scrcpy的鏡射螢幕功能連線到Android桌面。

ReDroid也是在電腦上用開源軟體跑Android APP的解決方案。因為別說雲手機了,很多Android手遊模擬器都是閉源軟體哪。相較之下,ReDroid除了ARM轉譯器以外都是開源的。更棒的是ReDroid支援GPU加速+ARM轉x86的轉譯器,這樣就可以玩大多數手機3D遊戲了。

對Linux用戶來說,這更是除了Waydroid以外,在Linux電腦高效率跑Android APP的方法。

本文將討論如何在x86架構的Linux電腦,用ReDroid玩ARM架構的手機遊戲。我們會在ReDroid映像檔加入ARM轉譯器+Google服務框架,以達成最佳使用體驗。

P.S. Windows和MacOS也可以跑Docker。

1. ReDroid需要用到的軟體

ReDroid在Github上有各大Linux發行版的安裝說明。

  1. 由於我是Arch Linux,因此Linux核心要先換成linux-zen以支援binderfs。
  2. 接著安裝docker和docker-compose,用於執行容器:
1
2
sudo pacman -S docker docker-compose
sudo systemctl enable --now docker
  1. 安裝ADB和Scrcpy,連線到Android桌面:
1
2
sudo pacman -S android-tools
sudo pacman -S scrcpy
  1. 下載用於傳輸音效的sndcpy,將其解壓縮。

*想要Scrcpy和Sndcpy二者整合和按鍵映射的,可以試試看QtScrcpy

2. 取得ARM轉譯器libndk

大部分電腦是x86架構,只能執行x86版的Android APP,然而很多手機遊戲只有ARM版。所以跑ReDroid前建議先下載ARM轉譯器,不然能執行的APP會少很多。

ReDroid支援用QEMU或libndk轉譯,本文採用後者。libndk是Google開發的ARM → x86轉譯器,含在Android Studio的模擬器裡。注意libndk是專有軟體,使用上處於灰色地帶。

  1. 你可以用Droid-NDK-Extractor指令稿從Android 11模擬器映像檔抽取libndk:
1
2
3
4
5
6
7
sudo pacman -S sleuthkit p7zip binwalk git

cd ~
git clone https://github.com/sickcodes/Droid-NDK-Extractor.git
cd Droid-NDK-Extractor
chmod +x android-extract-ndk.sh
./android-extract-ndk.sh x86_64
  1. 之後會在工作目錄的/working/extracted/下找到native-bridge.tar,大約30MB。
  2. 切換到native-bridge.tar所在的目錄,修正權限後重新壓縮檔案:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
cd ~/Droid-NDK-Extractor/working/extracted/
mkdir native-bridge
cd native-bridge
sudo tar -xpf ../native-bridge.tar
sudo chmod 0644 system/etc/init/ndk_translation_arm64.rc
sudo chmod 0755 system/bin/arm
sudo chmod 0755 system/bin/arm64
sudo chmod 0755 system/lib/arm
sudo chmod 0755 system/lib64/arm64
sudo chmod 0644 system/etc/binfmt_misc/*
sudo tar -cpf native-bridge.tar system
mv native-bridge.tar ..
cd ..
rm -r native-bridge
  1. 至此ARM轉譯器native-bridge.tar準備完成。

3. 建置ReDroid的Docker映像檔

ReDroid目前提供Android 8 ~ Android 13的映像檔。由於要使用libndk的關係,故以Android 11的映像檔為基礎下去建置。

  1. 新增一個空白目錄android,將剛剛製作的native-bridge.tar放到此目錄。
  2. android目錄新增一個名為dockerfile的檔案,填入以下內容:
1
2
3
FROM redroid/redroid:11.0.0-amd64

ADD native-bridge.tar /
  1. android目錄開啟終端機,開始建置含有libndk的ReDroid映像檔:
1
sudo docker build . -t redroid-11-libndk

4. 啟動ReDroid

如果要用ReDroid玩手機遊戲,建議電腦至少要有8GB以上RAM,因為有時ARM在轉譯成x86指令時會佔用大量RAM。

  1. 部署ReDroid。沒加--rm是因為後續還要對系統做修改。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
sudo docker run -itd --privileged \
    -v ~/data11-nb:/data \
    -p 5555:5555 \
    redroid-11-gapps-libndk \
    androidboot.redroid_gpu_mode=host \
    ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi \
    ro.product.cpu.abilist64=x86_64,arm64-v8a \
    ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi \
    ro.dalvik.vm.isa.arm=x86 \
    ro.dalvik.vm.isa.arm64=x86_64 \
    ro.enable.native.bridge.exec=1 \
    ro.dalvik.vm.native.bridge=libndk_translation.so \
    ro.ndk_translation.version=0.2.2
  1. 用ADB連線至本機的ReDroid:
1
2
3
4
5
adb connect localhost:5555

# 如果連不上,用以下指令看一下容器內部發生什麼問題
sudo docker logs <容器ID>
sudo docker exec <容器ID> logcat
  1. 執行Scrcpy,連線到Android桌面:
1
scrcpy -s localhost:5555
  1. 這樣就會看到Android的桌面了。
  2. 至於聲音的話,切換到sndcpy所在的目錄,執行sndcpy,開始串流音效:
1
./sndcpy localhost:5555

5. 安裝Google服務框架

作者說Google服務框架是專有軟體無法內建,那麼就得自行安裝了。第一個方法是重新編譯映像檔,第二個是手動安裝OpenGApps。

不推薦第一個方法,耗時而且作者提供的GApps編譯教學又有其他APP偵測不到的問題。

這裡採用第二個方法:手動安裝,過程參考自:Install GApps Manually – Google Groups

  1. OpenGapps下載x86_64架構的Android 11 GApps,選擇最小化的pico版。
  2. 解壓縮,會看到以下目錄
1
2
3
4
5
open_gapps-x86_64-11.0-pico-20220503
├── Core
├── GApps
├── META-INF
├── Optional
  1. 在解壓縮的目錄新增system目錄。
  2. 接著,將CoreGApps目錄裡面的.lz檔案都解壓縮,並將裡面的APK目錄按照對應的安裝目錄放到system目錄。例如GApps/googletts-x86_64/nodpi/app/下的GoogleTTS目錄要放到/system/app
  3. 放好之後,system下的目錄結構應該會長這樣:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
system
├── app
│   ├── GoogleCalendarSyncAdapter
│   │   └── GoogleCalendarSyncAdapter.apk
│   ├── GoogleContactsSyncAdapter
│   │   └── GoogleContactsSyncAdapter.apk
│   ├── GoogleExtShared
│   │   └── GoogleExtShared.apk
│   └── GoogleTTS
│       └── GoogleTTS.apk
├── etc
│   ├── default-permissions
│   │   ├── default-permissions.xml
│   │   └── opengapps-permissions-q.xml
│   ├── permissions
│   │   ├── com.google.android.dialer.support.xml
│   │   ├── com.google.android.maps.xml
│   │   ├── com.google.android.media.effects.xml
│   │   ├── privapp-permissions-google.xml
│   │   └── split-permissions-google.xml
│   ├── preferred-apps
│   │   └── google.xml
│   └── sysconfig
│       ├── dialer_experience.xml
│       ├── google_build.xml
│       ├── google_exclusives_enable.xml
│       ├── google-hiddenapi-package-whitelist.xml
│       └── google.xml
├── framework
│   ├── com.google.android.dialer.support.jar
│   ├── com.google.android.maps.jar
│   └── com.google.android.media.effects.jar
├── priv-app
│   ├── AndroidAutoPrebuiltStub
│   │   └── AndroidAutoPrebuiltStub.apk
│   ├── AndroidMigratePrebuilt
│   │   └── AndroidMigratePrebuilt.apk
│   ├── CarrierSetup
│   │   └── CarrierSetup.apk
│   ├── ConfigUpdater
│   │   └── ConfigUpdater.apk
│   ├── GoogleBackupTransport
│   │   └── GoogleBackupTransport.apk
│   ├── GoogleExtServices
│   │   └── GoogleExtServices.apk
│   ├── GoogleFeedback
│   │   └── GoogleFeedback.apk
│   ├── GoogleOneTimeInitializer
│   │   └── GoogleOneTimeInitializer.apk
│   ├── GooglePackageInstaller
│   │   └── GooglePackageInstaller.apk
│   ├── GooglePartnerSetup
│   │   └── GooglePartnerSetup.apk
│   ├── GoogleRestore
│   │   └── GoogleRestore.apk
│   ├── GoogleServicesFramework
│   │   └── GoogleServicesFramework.apk
│   ├── Phonesky
│   │   └── Phonesky.apk
│   ├── PrebuiltGmsCore
│   │   └── PrebuiltGmsCore.apk
│   └── SetupWizard
│       └── SetupWizard.apk
└── product
    └── overlay
        └── PlayStoreOverlay.apk
  1. 執行以下指令取得root權限:
1
2
3
4
adb connect localhost:5555
adb -s localhost:5555 root
adb -s localhost:5555 remount
adb -s localhost:5555 shell "rm -rf system/priv-app/PackageInstaller"
  1. 接著將system目錄推送到ReDroid系統,並賦予權限:
1
2
3
4
5
6
adb -s localhost:5555 push system /
adb -s localhost:5555 shell "pm grant com.google.android.gms android.permission.ACCESS_COARSE_LOCATION"
adb -s localhost:5555 shell "pm grant com.google.android.gms android.permission.ACCESS_FINE_LOCATION"
adb -s localhost:5555 shell "pm grant com.google.android.setupwizard android.permission.READ_PHONE_STATE"
adb -s localhost:5555 shell "pm grant com.google.android.setupwizard android.permission.READ_CONTACTS"
adb reboot
  1. 重新啟動ReDroid容器:
1
2
3
sudo docker ps

sudo docker restart <ReDroid的容器ID>
  1. 啟動Scrcpy,開啟系統設定→應用程式,點選右上角顯示系統應用程式,將Google Play服務和Play商店的權限都開啟。
  2. 執行以下指令取得Android裝置ID,到Google網站註冊裝置,等個30分鐘後重新啟動Redroid容器,才能登入Google Play。
1
2
3
adb -s localhost:5555 root
adb -s localhost:5555 shell 'sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \
    "select * from main where name = \"android_id\";"'

6. ReDroid安裝APK

目前即使有安裝libndk,Android 11的Play商店還是不給下載ARM架構的APP,請配合APKPure之類的應用程式商店安裝APP。

除了用容器內部的瀏覽器下載APK外,你還可以用ADB安裝APK至ReDroid容器。比方說到ApkMirror下載Line的APK,接著用ADB安裝:

1
adb -s localhost:5555 install "jp.naver.line.android.apk"

你也可以用ADB的pullpush指令傳輸檔案。

關於螢幕解析度的調整,請參閱ReDroidScrcpy的使用說明。

7. ReDroid如何「開關機」

如果要將ReDroid關機,將Scrcpy視窗關閉後,停止容器:

1
2
sudo docker ps
sudo docker stop <ReDroid的容器ID>

之後可以再用此指令啟動ReDroid。ReDroid容器的/data資料位於~/data11-nb目錄,可以用來備份多個系統的檔案。

1
2
3
4
5
6
7
sudo docker ps --filter "status=exited"

sudo docker start <ReDroid的容器ID>

adb connect localhost:5555

scrcpy -s localhost:5555

8. 總結

目前來看,ReDroid的泛用性比Waydroid好,因為不只造福Linux使用者,連那些想擺脫專有模擬器、想自架雲手機的用戶都可以利用ReDroid達成目的。