签名构建
生成密钥
在你的 Android 树的根目录下,运行这些命令,并修改 subject
行以反映你的信息
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/[email protected]'
mkdir ~/.android-certs
for cert in bluetooth cyngn-app media networkstack nfc platform releasekey sdk_sandbox shared testcert testkey verity; do \
./development/tools/make_key ~/.android-certs/$cert "$subject"; \
done
LineageOS 19.1 及更高版本还需要重新签名 APEX。每个 APEX 文件都使用两个密钥签名:一个用于 APEX 内的迷你文件系统镜像,另一个用于整个 APEX。在这种情况下,仅允许使用 SHA256_RSA4096 密钥,默认是 SHA256_RSA2048。因此,你需要复制 ./development/tools/make_key
文件并编辑以使用 SHA256_RSA4096。
cp ./development/tools/make_key ~/.android-certs/
sed -i 's|2048|4096|g' ~/.android-certs/make_key
然后生成 APEX 密钥,并修改下面的 subject
行以反映你的信息。
make_key
提示时按回车键来创建不带密码的签名密钥,但这会使你的私钥在被未经授权的个人访问时不受保护,你应该采取预防措施来保护它们和你的构建系统免受未经授权的访问。生成不带密码的密钥
对于生成的每个 APEX 密钥,你需要输入两次空白密码短语。
for apex in com.android.adbd com.android.adservices com.android.adservices.api com.android.appsearch com.android.appsearch.apk com.android.art com.android.bluetooth com.android.btservices com.android.cellbroadcast com.android.compos com.android.configinfrastructure com.android.connectivity.resources com.android.conscrypt com.android.devicelock com.android.extservices com.android.graphics.pdf com.android.hardware.authsecret com.android.hardware.biometrics.face.virtual com.android.hardware.biometrics.fingerprint.virtual com.android.hardware.boot com.android.hardware.cas com.android.hardware.neuralnetworks com.android.hardware.rebootescrow com.android.hardware.wifi com.android.healthfitness com.android.hotspot2.osulogin com.android.i18n com.android.ipsec com.android.media com.android.media.swcodec com.android.mediaprovider com.android.nearby.halfsheet com.android.networkstack.tethering com.android.neuralnetworks com.android.nfcservices com.android.ondevicepersonalization com.android.os.statsd com.android.permission com.android.profiling com.android.resolv com.android.rkpd com.android.runtime com.android.safetycenter.resources com.android.scheduling com.android.sdkext com.android.support.apexer com.android.telephony com.android.telephonymodules com.android.tethering com.android.tzdata com.android.uwb com.android.uwb.resources com.android.virt com.android.vndk.current com.android.vndk.current.on_vendor com.android.wifi com.android.wifi.dialog com.android.wifi.resources com.google.pixel.camera.hal com.google.pixel.vibrator.hal com.qorvo.uwb; do \
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN='$apex'/[email protected]'; \
~/.android-certs/make_key ~/.android-certs/$apex "$subject"; \
openssl pkcs8 -in ~/.android-certs/$apex.pk8 -inform DER -nocrypt -out ~/.android-certs/$apex.pem; \
done
你应该妥善保管这些密钥。
生成带密码的密钥
对于生成的每个 APEX 密钥,你需要输入两次密码短语。
for apex in com.android.adbd com.android.adservices com.android.adservices.api com.android.appsearch com.android.appsearch.apk com.android.art com.android.bluetooth com.android.btservices com.android.cellbroadcast com.android.compos com.android.configinfrastructure com.android.connectivity.resources com.android.conscrypt com.android.devicelock com.android.extservices com.android.graphics.pdf com.android.hardware.authsecret com.android.hardware.biometrics.face.virtual com.android.hardware.biometrics.fingerprint.virtual com.android.hardware.boot com.android.hardware.cas com.android.hardware.neuralnetworks com.android.hardware.rebootescrow com.android.hardware.wifi com.android.healthfitness com.android.hotspot2.osulogin com.android.i18n com.android.ipsec com.android.media com.android.media.swcodec com.android.mediaprovider com.android.nearby.halfsheet com.android.networkstack.tethering com.android.neuralnetworks com.android.nfcservices com.android.ondevicepersonalization com.android.os.statsd com.android.permission com.android.profiling com.android.resolv com.android.rkpd com.android.runtime com.android.safetycenter.resources com.android.scheduling com.android.sdkext com.android.support.apexer com.android.telephony com.android.telephonymodules com.android.tethering com.android.tzdata com.android.uwb com.android.uwb.resources com.android.virt com.android.vndk.current com.android.vndk.current.on_vendor com.android.wifi com.android.wifi.dialog com.android.wifi.resources com.google.pixel.camera.hal com.google.pixel.vibrator.hal com.qorvo.uwb; do \
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN='$apex'/[email protected]'; \
~/.android-certs/make_key ~/.android-certs/$apex "$subject"; \
openssl pkcs8 -in ~/.android-certs/$apex.pk8 -inform DER -out ~/.android-certs/$apex.pem; \
done
你应该妥善保管这些密钥,并将密码短语存储在安全的位置。
生成安装包
生成和签名目标文件
在按照你设备的构建说明操作后,不要运行 brunch <codename>
,而是运行以下命令
breakfast <codename>
mka target-files-package otatools
坐下来等待一段时间 - 这可能需要一段时间,具体取决于你电脑的配置。完成后,你只需要签名所有 APK 和 APEX。
你可以设置 ANDROID_PW_FILE
和 EDITOR
环境变量,以便更方便地输入证书密码。当你调用 sign_target_files_apks
命令时,如果 ANDROID_PW_FILE
文件缺少任何相关的证书密码,编辑器将打开该文件。
# Enter key passwords between the [[[ ]]] brackets
# (Additional spaces are harmless)
[[[ ]]] ~/.android-certs/bluetooth
[[[ ]]] ~/.android-certs/com.android.adbd
etc...
填写缺失的密码,保存文件,然后关闭编辑器。确保将此文件存储在安全的位置。
LineageOS 18.1 及更低版本的签名过程
croot
sign_target_files_apks -o -d ~/.android-certs \
$OUT/obj/PACKAGING/target_files_intermediates/*-target_files-*.zip \
signed-target_files.zip
LineageOS 19.1 及更高版本的签名过程
croot
sign_target_files_apks -o -d ~/.android-certs \
--extra_apks AdServicesApk.apk=$HOME/.android-certs/releasekey \
--extra_apks FederatedCompute.apk=$HOME/.android-certs/releasekey \
--extra_apks HalfSheetUX.apk=$HOME/.android-certs/releasekey \
--extra_apks HealthConnectBackupRestore.apk=$HOME/.android-certs/releasekey \
--extra_apks HealthConnectController.apk=$HOME/.android-certs/releasekey \
--extra_apks OsuLogin.apk=$HOME/.android-certs/releasekey \
--extra_apks SafetyCenterResources.apk=$HOME/.android-certs/releasekey \
--extra_apks ServiceConnectivityResources.apk=$HOME/.android-certs/releasekey \
--extra_apks ServiceUwbResources.apk=$HOME/.android-certs/releasekey \
--extra_apks ServiceWifiResources.apk=$HOME/.android-certs/releasekey \
--extra_apks WifiDialog.apk=$HOME/.android-certs/releasekey \
--extra_apks com.android.adbd.apex=$HOME/.android-certs/com.android.adbd \
--extra_apks com.android.adservices.apex=$HOME/.android-certs/com.android.adservices \
--extra_apks com.android.adservices.api.apex=$HOME/.android-certs/com.android.adservices.api \
--extra_apks com.android.appsearch.apex=$HOME/.android-certs/com.android.appsearch \
--extra_apks com.android.appsearch.apk.apex=$HOME/.android-certs/com.android.appsearch.apk \
--extra_apks com.android.art.apex=$HOME/.android-certs/com.android.art \
--extra_apks com.android.bluetooth.apex=$HOME/.android-certs/com.android.bluetooth \
--extra_apks com.android.btservices.apex=$HOME/.android-certs/com.android.btservices \
--extra_apks com.android.cellbroadcast.apex=$HOME/.android-certs/com.android.cellbroadcast \
--extra_apks com.android.compos.apex=$HOME/.android-certs/com.android.compos \
--extra_apks com.android.configinfrastructure.apex=$HOME/.android-certs/com.android.configinfrastructure \
--extra_apks com.android.connectivity.resources.apex=$HOME/.android-certs/com.android.connectivity.resources \
--extra_apks com.android.conscrypt.apex=$HOME/.android-certs/com.android.conscrypt \
--extra_apks com.android.devicelock.apex=$HOME/.android-certs/com.android.devicelock \
--extra_apks com.android.extservices.apex=$HOME/.android-certs/com.android.extservices \
--extra_apks com.android.graphics.pdf.apex=$HOME/.android-certs/com.android.graphics.pdf \
--extra_apks com.android.hardware.authsecret.apex=$HOME/.android-certs/com.android.hardware.authsecret \
--extra_apks com.android.hardware.biometrics.face.virtual.apex=$HOME/.android-certs/com.android.hardware.biometrics.face.virtual \
--extra_apks com.android.hardware.biometrics.fingerprint.virtual.apex=$HOME/.android-certs/com.android.hardware.biometrics.fingerprint.virtual \
--extra_apks com.android.hardware.boot.apex=$HOME/.android-certs/com.android.hardware.boot \
--extra_apks com.android.hardware.cas.apex=$HOME/.android-certs/com.android.hardware.cas \
--extra_apks com.android.hardware.neuralnetworks.apex=$HOME/.android-certs/com.android.hardware.neuralnetworks \
--extra_apks com.android.hardware.rebootescrow.apex=$HOME/.android-certs/com.android.hardware.rebootescrow \
--extra_apks com.android.hardware.wifi.apex=$HOME/.android-certs/com.android.hardware.wifi \
--extra_apks com.android.healthfitness.apex=$HOME/.android-certs/com.android.healthfitness \
--extra_apks com.android.hotspot2.osulogin.apex=$HOME/.android-certs/com.android.hotspot2.osulogin \
--extra_apks com.android.i18n.apex=$HOME/.android-certs/com.android.i18n \
--extra_apks com.android.ipsec.apex=$HOME/.android-certs/com.android.ipsec \
--extra_apks com.android.media.apex=$HOME/.android-certs/com.android.media \
--extra_apks com.android.media.swcodec.apex=$HOME/.android-certs/com.android.media.swcodec \
--extra_apks com.android.mediaprovider.apex=$HOME/.android-certs/com.android.mediaprovider \
--extra_apks com.android.nearby.halfsheet.apex=$HOME/.android-certs/com.android.nearby.halfsheet \
--extra_apks com.android.networkstack.tethering.apex=$HOME/.android-certs/com.android.networkstack.tethering \
--extra_apks com.android.neuralnetworks.apex=$HOME/.android-certs/com.android.neuralnetworks \
--extra_apks com.android.nfcservices.apex=$HOME/.android-certs/com.android.nfcservices \
--extra_apks com.android.ondevicepersonalization.apex=$HOME/.android-certs/com.android.ondevicepersonalization \
--extra_apks com.android.os.statsd.apex=$HOME/.android-certs/com.android.os.statsd \
--extra_apks com.android.permission.apex=$HOME/.android-certs/com.android.permission \
--extra_apks com.android.profiling.apex=$HOME/.android-certs/com.android.profiling \
--extra_apks com.android.resolv.apex=$HOME/.android-certs/com.android.resolv \
--extra_apks com.android.rkpd.apex=$HOME/.android-certs/com.android.rkpd \
--extra_apks com.android.runtime.apex=$HOME/.android-certs/com.android.runtime \
--extra_apks com.android.safetycenter.resources.apex=$HOME/.android-certs/com.android.safetycenter.resources \
--extra_apks com.android.scheduling.apex=$HOME/.android-certs/com.android.scheduling \
--extra_apks com.android.sdkext.apex=$HOME/.android-certs/com.android.sdkext \
--extra_apks com.android.support.apexer.apex=$HOME/.android-certs/com.android.support.apexer \
--extra_apks com.android.telephony.apex=$HOME/.android-certs/com.android.telephony \
--extra_apks com.android.telephonymodules.apex=$HOME/.android-certs/com.android.telephonymodules \
--extra_apks com.android.tethering.apex=$HOME/.android-certs/com.android.tethering \
--extra_apks com.android.tzdata.apex=$HOME/.android-certs/com.android.tzdata \
--extra_apks com.android.uwb.apex=$HOME/.android-certs/com.android.uwb \
--extra_apks com.android.uwb.resources.apex=$HOME/.android-certs/com.android.uwb.resources \
--extra_apks com.android.virt.apex=$HOME/.android-certs/com.android.virt \
--extra_apks com.android.vndk.current.apex=$HOME/.android-certs/com.android.vndk.current \
--extra_apks com.android.vndk.current.on_vendor.apex=$HOME/.android-certs/com.android.vndk.current.on_vendor \
--extra_apks com.android.wifi.apex=$HOME/.android-certs/com.android.wifi \
--extra_apks com.android.wifi.dialog.apex=$HOME/.android-certs/com.android.wifi.dialog \
--extra_apks com.android.wifi.resources.apex=$HOME/.android-certs/com.android.wifi.resources \
--extra_apks com.google.pixel.camera.hal.apex=$HOME/.android-certs/com.google.pixel.camera.hal \
--extra_apks com.google.pixel.vibrator.hal.apex=$HOME/.android-certs/com.google.pixel.vibrator.hal \
--extra_apks com.qorvo.uwb.apex=$HOME/.android-certs/com.qorvo.uwb \
--extra_apex_payload_key com.android.adbd.apex=$HOME/.android-certs/com.android.adbd.pem \
--extra_apex_payload_key com.android.adservices.apex=$HOME/.android-certs/com.android.adservices.pem \
--extra_apex_payload_key com.android.adservices.api.apex=$HOME/.android-certs/com.android.adservices.api.pem \
--extra_apex_payload_key com.android.appsearch.apex=$HOME/.android-certs/com.android.appsearch.pem \
--extra_apex_payload_key com.android.appsearch.apk.apex=$HOME/.android-certs/com.android.appsearch.apk.pem \
--extra_apex_payload_key com.android.art.apex=$HOME/.android-certs/com.android.art.pem \
--extra_apex_payload_key com.android.bluetooth.apex=$HOME/.android-certs/com.android.bluetooth.pem \
--extra_apex_payload_key com.android.btservices.apex=$HOME/.android-certs/com.android.btservices.pem \
--extra_apex_payload_key com.android.cellbroadcast.apex=$HOME/.android-certs/com.android.cellbroadcast.pem \
--extra_apex_payload_key com.android.compos.apex=$HOME/.android-certs/com.android.compos.pem \
--extra_apex_payload_key com.android.configinfrastructure.apex=$HOME/.android-certs/com.android.configinfrastructure.pem \
--extra_apex_payload_key com.android.connectivity.resources.apex=$HOME/.android-certs/com.android.connectivity.resources.pem \
--extra_apex_payload_key com.android.conscrypt.apex=$HOME/.android-certs/com.android.conscrypt.pem \
--extra_apex_payload_key com.android.devicelock.apex=$HOME/.android-certs/com.android.devicelock.pem \
--extra_apex_payload_key com.android.extservices.apex=$HOME/.android-certs/com.android.extservices.pem \
--extra_apex_payload_key com.android.graphics.pdf.apex=$HOME/.android-certs/com.android.graphics.pdf.pem \
--extra_apex_payload_key com.android.hardware.authsecret.apex=$HOME/.android-certs/com.android.hardware.authsecret.pem \
--extra_apex_payload_key com.android.hardware.biometrics.face.virtual.apex=$HOME/.android-certs/com.android.hardware.biometrics.face.virtual.pem \
--extra_apex_payload_key com.android.hardware.biometrics.fingerprint.virtual.apex=$HOME/.android-certs/com.android.hardware.biometrics.fingerprint.virtual.pem \
--extra_apex_payload_key com.android.hardware.boot.apex=$HOME/.android-certs/com.android.hardware.boot.pem \
--extra_apex_payload_key com.android.hardware.cas.apex=$HOME/.android-certs/com.android.hardware.cas.pem \
--extra_apex_payload_key com.android.hardware.neuralnetworks.apex=$HOME/.android-certs/com.android.hardware.neuralnetworks.pem \
--extra_apex_payload_key com.android.hardware.rebootescrow.apex=$HOME/.android-certs/com.android.hardware.rebootescrow.pem \
--extra_apex_payload_key com.android.hardware.wifi.apex=$HOME/.android-certs/com.android.hardware.wifi.pem \
--extra_apex_payload_key com.android.healthfitness.apex=$HOME/.android-certs/com.android.healthfitness.pem \
--extra_apex_payload_key com.android.hotspot2.osulogin.apex=$HOME/.android-certs/com.android.hotspot2.osulogin.pem \
--extra_apex_payload_key com.android.i18n.apex=$HOME/.android-certs/com.android.i18n.pem \
--extra_apex_payload_key com.android.ipsec.apex=$HOME/.android-certs/com.android.ipsec.pem \
--extra_apex_payload_key com.android.media.apex=$HOME/.android-certs/com.android.media.pem \
--extra_apex_payload_key com.android.media.swcodec.apex=$HOME/.android-certs/com.android.media.swcodec.pem \
--extra_apex_payload_key com.android.mediaprovider.apex=$HOME/.android-certs/com.android.mediaprovider.pem \
--extra_apex_payload_key com.android.nearby.halfsheet.apex=$HOME/.android-certs/com.android.nearby.halfsheet.pem \
--extra_apex_payload_key com.android.networkstack.tethering.apex=$HOME/.android-certs/com.android.networkstack.tethering.pem \
--extra_apex_payload_key com.android.neuralnetworks.apex=$HOME/.android-certs/com.android.neuralnetworks.pem \
--extra_apex_payload_key com.android.nfcservices.apex=$HOME/.android-certs/com.android.nfcservices.pem \
--extra_apex_payload_key com.android.ondevicepersonalization.apex=$HOME/.android-certs/com.android.ondevicepersonalization.pem \
--extra_apex_payload_key com.android.os.statsd.apex=$HOME/.android-certs/com.android.os.statsd.pem \
--extra_apex_payload_key com.android.permission.apex=$HOME/.android-certs/com.android.permission.pem \
--extra_apex_payload_key com.android.profiling.apex=$HOME/.android-certs/com.android.profiling.pem \
--extra_apex_payload_key com.android.resolv.apex=$HOME/.android-certs/com.android.resolv.pem \
--extra_apex_payload_key com.android.rkpd.apex=$HOME/.android-certs/com.android.rkpd.pem \
--extra_apex_payload_key com.android.runtime.apex=$HOME/.android-certs/com.android.runtime.pem \
--extra_apex_payload_key com.android.safetycenter.resources.apex=$HOME/.android-certs/com.android.safetycenter.resources.pem \
--extra_apex_payload_key com.android.scheduling.apex=$HOME/.android-certs/com.android.scheduling.pem \
--extra_apex_payload_key com.android.sdkext.apex=$HOME/.android-certs/com.android.sdkext.pem \
--extra_apex_payload_key com.android.support.apexer.apex=$HOME/.android-certs/com.android.support.apexer.pem \
--extra_apex_payload_key com.android.telephony.apex=$HOME/.android-certs/com.android.telephony.pem \
--extra_apex_payload_key com.android.telephonymodules.apex=$HOME/.android-certs/com.android.telephonymodules.pem \
--extra_apex_payload_key com.android.tethering.apex=$HOME/.android-certs/com.android.tethering.pem \
--extra_apex_payload_key com.android.tzdata.apex=$HOME/.android-certs/com.android.tzdata.pem \
--extra_apex_payload_key com.android.uwb.apex=$HOME/.android-certs/com.android.uwb.pem \
--extra_apex_payload_key com.android.uwb.resources.apex=$HOME/.android-certs/com.android.uwb.resources.pem \
--extra_apex_payload_key com.android.virt.apex=$HOME/.android-certs/com.android.virt.pem \
--extra_apex_payload_key com.android.vndk.current.apex=$HOME/.android-certs/com.android.vndk.current.pem \
--extra_apex_payload_key com.android.vndk.current.on_vendor.apex=$HOME/.android-certs/com.android.vndk.current.on_vendor.pem \
--extra_apex_payload_key com.android.wifi.apex=$HOME/.android-certs/com.android.wifi.pem \
--extra_apex_payload_key com.android.wifi.dialog.apex=$HOME/.android-certs/com.android.wifi.dialog.pem \
--extra_apex_payload_key com.android.wifi.resources.apex=$HOME/.android-certs/com.android.wifi.resources.pem \
--extra_apex_payload_key com.google.pixel.camera.hal.apex=$HOME/.android-certs/com.google.pixel.camera.hal.pem \
--extra_apex_payload_key com.google.pixel.vibrator.hal.apex=$HOME/.android-certs/com.google.pixel.vibrator.hal.pem \
--extra_apex_payload_key com.qorvo.uwb.apex=$HOME/.android-certs/com.qorvo.uwb.pem \
$OUT/obj/PACKAGING/target_files_intermediates/*-target_files*.zip \
signed-target_files.zip
生成安装包
现在,要生成可安装的 zip 文件,运行
ota_from_target_files -k ~/.android-certs/releasekey \
--block --backup=true \
signed-target_files.zip \
signed-ota_update.zip
然后,像往常一样在 recovery 中安装 zip 文件。
更改密钥
使用迁移构建版本
你可以通过运行以下命令来设置你自己的迁移构建版本
LineageOS 22.1
repopick -f 413919
LineageOS 21.0
repopick -f 399285
LineageOS 20.0
repopick -f 380125
LineageOS 19.1
repopick -f 327460
LineageOS 18.1
repopick -f 297539
LineageOS 17.1
repopick -f 266939
LineageOS 16.0
repopick -f 239520
LineageOS 15.1
repopick -f 192655 -P vendor/lineage
repopick -f 192656 -P frameworks/base
LineageOS 14.1
repopick -f 156047 162144
LineageOS 13.0
repopick -f 160272
然后,按照 生成安装包的说明 进行操作。
返回
安装迁移构建版本后,你可以切换回构建正常的构建版本
LineageOS 19.1
cd frameworks/base
git reset --hard github/lineage-19.1
LineageOS 18.1
cd frameworks/base
git reset --hard github/lineage-18.1
LineageOS 17.1
cd frameworks/base
git reset --hard github/lineage-17.1
LineageOS 16.0
cd frameworks/base
git reset --hard github/lineage-16.0
LineageOS 15.1
cd vendor/lineage
git reset --hard github/lineage-15.1
croot
cd frameworks/base
git reset --hard github/lineage-15.1
LineageOS 14.1
cd vendor/cm
git reset --hard github/cm-14.1
croot
cd frameworks/base
git reset --hard github/cm-14.1
使用脚本
你也可以使用脚本或小型可刷写的 zip 文件,它们被设计为在新密钥构建版本安装之前运行一次。该脚本位于 ./lineage/scripts/key-migration/migration.sh
该脚本也可以制作成 zip 文件,方法是将其插入类似于 这个 的 zip 文件中。此 zip 文件将脚本放置在 META-INF/com/google/android/update-binary
中,并添加了一些内容以将状态消息打印到 recovery 中。
从测试密钥到官方密钥或反之
如果你从测试密钥构建版本(例如 “未签名” 的非官方构建版本)迁移到官方 LineageOS 构建版本,你可以将脚本推送到你的设备并在 Android 中运行它
adb root # This requires an userdebug/eng build and ADB root access to be enabled
adb shell stop
adb push ./lineage/scripts/key-migration/migration.sh /data/local/tmp/migration.sh
adb shell sh /data/local/tmp/migration.sh official
adb reboot recovery
# Now install the official LineageOS install zip
如果你从官方构建版本迁移到你自己的 “未签名” 构建版本,你可以用相同的方式运行脚本,但使用参数 “unofficial” 而不是 “official”。
从测试密钥到你自己的发布密钥或反之
如果你从测试密钥迁移到你自己的签名构建版本,你可以将你自己的密钥添加到脚本中。首先,通过运行 ./lineage/scripts/key-migration/export-keys.sh
中的脚本,将你的密钥导出为所需的格式。
这将在终端中以所需的格式打印密钥和证书。接下来,编辑脚本以使用你的密钥。你需要注释掉(通过在前面添加 #
),或删除 “release” 密钥和证书的现有定义。现在,将你上面的输出复制并粘贴到脚本中之前行的位置。确保保持 “test” 密钥和证书定义不变。
你的脚本已准备就绪!将其推送到设备并运行它,以与上面描述的相同方式,然后安装你自己的签名 zip 文件。如果你需要反向运行此操作,只需使用 “unofficial” 参数而不是 “official”,你的密钥将被替换为官方密钥。