<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; Android</title>
	<atom:link href="https://zohead.com/archives/category/technology/android/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>酷走行车记录仪root和GPS时间同步</title>
		<link>https://zohead.com/archives/kuzo-root-gps-time-sync/</link>
		<comments>https://zohead.com/archives/kuzo-root-gps-time-sync/#comments</comments>
		<pubDate>Sat, 26 Feb 2022 13:30:33 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[GPS]]></category>
		<category><![CDATA[root]]></category>
		<category><![CDATA[SuperSU]]></category>
		<category><![CDATA[时间同步]]></category>
		<category><![CDATA[行车记录仪]]></category>
		<category><![CDATA[酷走]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1734</guid>
		<description><![CDATA[首先很惭愧我已经有一年多没有更新博客内容了，之前写过一篇 酷走 Android 行车记录仪研究 的文章，行车记录仪的 ADB 调试已经可以用起来了，不过还存在网络不可用时系统时间不正确的问题，刚好再研究一下行车记录仪的 root 以及通过 GPS 同步记录仪的系统时间。 root 系统 其实酷走这款 Android 行车记录仪系统已经自带了“假” root 权限，只是没有带 su 管理的工具。虽然 SuperSU 很久没更新了，我还是准备安装试试，首先从 SuperSU 官网下载最新 2.82 版本的刷机 zip 包： http://supersuroot.org/downloads/Supe [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>首先很惭愧我已经有一年多没有更新博客内容了，之前写过一篇 <a href="https://zohead.com/archives/kuzo-android-internal/" target="_blank">酷走 Android 行车记录仪研究</a> 的文章，行车记录仪的 ADB 调试已经可以用起来了，不过还存在网络不可用时系统时间不正确的问题，刚好再研究一下行车记录仪的 root 以及通过 GPS 同步记录仪的系统时间。</p>
<h2 id="root-system">root 系统</h2>
<p>其实酷走这款 Android 行车记录仪系统已经自带了“假” root 权限，只是没有带 su 管理的工具。虽然 SuperSU 很久没更新了，我还是准备安装试试，首先从 SuperSU 官网下载最新 2.82 版本的刷机 zip 包：</p>
<pre><code>http://supersuroot.org/downloads/SuperSU-v2.82-201705271822.zip
</code></pre>
<p>解压缩之后，首先使用 adb 命令将系统重新挂载为可读写状态：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb remount
</pre>
<p>参考之前折腾刷机的经验，先拷贝刷机 zip 包中的文件：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb push common/Superuser.apk /sdcard/
~$ adb push common/install-recovery.sh /sdcard/
~$ adb push x86/su /sdcard/
~$ adb push x86/supolicy /sdcard/
~$ adb push x86/libsupol.so /sdcard/
</pre>
<p>这个时候可以使用 <code>adb shell</code> 命令登录到行车记录仪，再将文件移动到系统目录：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# mkdir /system/app/SuperSU
root@Sf3gr_mrd6_p2_720# mv /sdcard/Superuser.apk /system/app/SuperSU/
root@Sf3gr_mrd6_p2_720# mv /sdcard/install-recovery.sh /system/bin/
root@Sf3gr_mrd6_p2_720# cp /sdcard/su /system/xbin/
root@Sf3gr_mrd6_p2_720# mkdir /system/bin/.ext
root@Sf3gr_mrd6_p2_720# cp /sdcard/su /system/bin/.ext/.su
root@Sf3gr_mrd6_p2_720# mv /sdcard/su /system/xbin/daemonsu
root@Sf3gr_mrd6_p2_720# mv /sdcard/supolicy /system/xbin/
root@Sf3gr_mrd6_p2_720# mv /sdcard/libsupol.so /system/lib/
</pre>
<p>还需要设置文件权限：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# chmod 0644 /system/app/SuperSU/Superuser.apk
root@Sf3gr_mrd6_p2_720# chmod 0755 /system/bin/install-recovery.sh /system/xbin/su /system/bin/.ext/.su /system/xbin/daemonsu /system/xbin/supolicy /system/lib/libsupol.so
root@Sf3gr_mrd6_p2_720# chcon u:object_r:system_file:s0 /system/app/SuperSU/Superuser.apk /system/bin/install-recovery.sh /system/xbin/su /system/bin/.ext/.su /system/xbin/daemonsu /system/xbin/supolicy /system/lib/libsupol.so
</pre>
<p>不过最后运行 SuperSu 安装命令的时候却出错了：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# /system/xbin/su --install
error: only position independent executables (PIE) are supported.
</pre>
<p>看来是想当然了，我拷贝的是非 PIE 格式的 su 程序导致运行不了。那就重新拷贝正确的 su 程序（需要使用 <code>su.pie</code> 地址无关的可执行程序版本），这次就 adb 命令一步到位了：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb push common/Superuser.apk /system/app/SuperSU/Superuser.apk
~$ adb push common/install-recovery.sh /system/bin/install-recovery.sh
~$ adb push x86/su.pie /system/xbin/su
~$ adb push x86/su.pie /system/bin/.ext/.su
~$ adb push x86/su.pie /system/xbin/daemonsu
~$ adb push x86/supolicy /system/xbin/supolicy
~$ adb push x86/libsupol.so /system/lib/libsupol.so
</pre>
<p>设置文件权限之后再重新执行 <code>/system/xbin/su --install</code> 命令安装就没有问题了，测试确认 SuperSU App 也可以正常运行了。</p>
<h2 id="gps-time-sync">GPS 时间同步</h2>
<p>为了能修正行车记录仪的系统时间，我在行车记录仪上安装了 <a href="https://play.google.com/store/apps/details?id=com.pautinanet.smarttimesync" target="_blank">Smart Time Sync</a> 这款 App 进行时间同步。Smart Time Sync 支持通过 GPS、NTP  或 HTTP 同步系统时间，不过行车记录仪上都没有装 SIM 卡更没有网络，只能通过 GPS 进行时间同步了。</p>
<p>如果 Android 系统没有 root，Smart Time Sync 只能进行手工同步，因此为了实际使用过程中能实现后台自动时间同步，上面的系统 root 操作还是必须的。</p>
<p>Smart Time Sync 运行效果如下：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372134/kuzo-smart-time-sync.png" alt="Smart Time Sync"></p>
<p>App 使用起来也很简单，首先在 Source 源选择 <code>GPS</code>，然后点 <code>Sync Time</code> 按钮就可以手工同步系统时间了，当然前提是设备必须放在有 GPS 信号的地方。</p>
<p>点击后面的设置图标就可以配置后台自动进行时间同步了，现在我的行车记录仪即使掉电了保存的录像文件时间也是正确的了。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kuzo-root-gps-time-sync/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>酷走Android行车记录仪研究</title>
		<link>https://zohead.com/archives/kuzo-android-internal/</link>
		<comments>https://zohead.com/archives/kuzo-android-internal/#comments</comments>
		<pubDate>Wed, 03 Jul 2019 17:04:56 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[ADB]]></category>
		<category><![CDATA[Atom]]></category>
		<category><![CDATA[baksmali]]></category>
		<category><![CDATA[oat2dex]]></category>
		<category><![CDATA[Vysor]]></category>
		<category><![CDATA[行车记录仪]]></category>
		<category><![CDATA[酷走]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1586</guid>
		<description><![CDATA[还是之前用的酷走 Android 行车记录仪，最近放在车上使用倒挺顺畅，不过也有一些明显的问题（例如 SIM 卡未联网时系统时间不正确等），因此想着最好能找到调试这款 Android 行车记录仪的途径。 ADB 调试 最开始想到这款行车记录仪既然是用的 Android 系统，应该是支持 ADB 调试功能的，将行车记录仪的 USB 接口直接用数据线接到我的笔记本电脑上，却没有找到 ADB 设备。以为记录仪内部是不是有一个专用的调试串口之类的，为此找到硬件同事把记录仪拆解了一番： 不过并没有找到除 USB 之外其它的调试接口，这时无意将记录仪通过 USB 接到另一台主机上，却认到 ADB 设备了。 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>还是之前用的<a href="https://zohead.com/archives/kuzo-recorder/">酷走 Android 行车记录仪</a>，最近放在车上使用倒挺顺畅，不过也有一些明显的问题（例如 SIM 卡未联网时系统时间不正确等），因此想着最好能找到调试这款 Android 行车记录仪的途径。</p>
<h2 id="adb-debug">ADB 调试</h2>
<p>最开始想到这款行车记录仪既然是用的 Android 系统，应该是支持 ADB 调试功能的，将行车记录仪的 USB 接口直接用数据线接到我的笔记本电脑上，却没有找到 ADB 设备。以为记录仪内部是不是有一个专用的调试串口之类的，为此找到硬件同事把记录仪拆解了一番：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737372135/kuzo-teardown.jpg" alt="酷走行车记录仪拆解" title="酷走行车记录仪拆解"></p>
<p>不过并没有找到除 USB 之外其它的调试接口，这时无意将记录仪通过 USB 接到另一台主机上，却认到 ADB 设备了。无奈地认识到自己犯蠢没有用别的机器验证是否和 USB 接口有关系，毕竟把记录仪拆解了上面的样子也基本不可能装回去咯 -_-#。初步估计我的老笔记本 USB 接口供电有问题，记录仪通过 USB 插到 PC 机之后没有独立供电也只能从 PC 机 USB 接口取电。</p>
<p>比较好的消息是我日常使用的 Chromebook 接记录仪也能认到 ADB 设备，这样就能直接通过 <a href="https://chrome.google.com/webstore/detail/vysor/gidgenkbbabolejbgbpnhbimgjbffefm" target="_blank">Vysor</a> 这个 Chrome 应用来访问行车记录仪了，<a href="http://vysor.io/" target="_blank">Vysor</a> 官网上也有 Windows、Mac、Linux 系统下的桌面版本。另外为了方便通过 ADB 访问行车记录仪的 shell 以及上传下载文件之类的，我还在 Chromebook 的 <a href="https://github.com/dnschneid/crouton" target="_blank">crouton</a> 环境中安装了 adb 支持。</p>
<p>启动 Vysor Chrome 应用，授权允许访问 ADB 设备之后，应该就能看到记录仪设备了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442964/vysor-devices.png" alt="行车记录仪 ADB 设备" title="行车记录仪 ADB 设备"></p>
<p>上图中的 <code>Full AOSP on Sofia</code> 就是行车记录仪设备，点击 Vysor 上的 <code>View</code> 按钮会自动通过 ADB 在设备上安装 Vysor 受控端 App，稍等片刻就可以看到记录仪的 Android 系统主界面咯：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371940/kuzo-android-ui.png" alt="行车记录仪 Android 主界面" title="行车记录仪 Android 主界面"></p>
<blockquote>
<p><strong>注意</strong></p>
<p>由于通过 USB 数据线将行车记录仪连接到电脑时功率一般达不到要求的 5V / 2A，记录仪自带的 250 mAh 电池续航时间也有限，因此 ADB 调试操作前最好先使用标准 5V / 2A 充电器将记录仪充满电。</p>
</blockquote>
<h2 id="system-hardware">系统和硬件信息</h2>
<p>解锁进入系统之后，可以先看看我这台行车记录仪的系统版本信息，看起来比较明显用的是 Android 5.1.1 AOSP 版本，内核版本为 3.14.0，版本号为 2.1.5：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737371891/kuzo-android-about.png" alt="行车记录仪 Android 版本" title="行车记录仪 Android 版本"></p>
<p>值得一提的是另外一台同样型号的酷走行车记录仪，通过 Vysor Chrome 应用能找到 ADB 设备，但是自动安装 Vysor App 之后无法正确显示 Android 系统主界面，这样只能通过 ADB Shell 来获取系统版本信息了：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ adb shell getprop | grep 'ro\.build\.'
[ro.build.characteristics]: [tablet]
[ro.build.date.utc]: [1473825617]
[ro.build.date]: [Wed Sep 14 12:00:17 CST 2016]
[ro.build.description]: [Sf3gr_mrd6_p2_720-userdebug 5.1.1 LMY47V eng.softteam.20160914.115752 release-keys]
[ro.build.display.id]: [2.2.3]
[ro.build.fingerprint]: [Intel/Sf3gr_mrd6_p2_720/5.1.1/LMY47V/softteam09141159:userdebug/release-keys]
[ro.build.flavor]: [Sf3gr_mrd6_p2_720-userdebug]
[ro.build.host]: [pdd-build]
[ro.build.id]: [LMY47V]
[ro.build.product]: [Sf3gr_mrd6_p2_720]
[ro.build.tags]: [release-keys]
[ro.build.type]: [userdebug]
[ro.build.user]: [softteam]
[ro.build.version.all_codenames]: [REL]
[ro.build.version.base_os]: []
[ro.build.version.codename]: [REL]
[ro.build.version.incremental]: [eng.softteam.20160914.115752]
[ro.build.version.release]: [5.1.1]
[ro.build.version.sdk]: [22]
[ro.build.version.security_patch]: [2015-11-01]
</pre>
<p>可以看到这台记录仪的版本号是新一点的 2.2.3，进入 Shell 查看内核版本：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ uname -a
Linux localhost 3.14.0 #1 SMP PREEMPT Wed Sep 14 12:14:50 CST 2016 i686 GNU/Linux
</pre>
<p>记录仪使用的是 Intel SOFIA Atom x3 超低功耗 x86 处理器，下面的 <code>/proc/cpuinfo</code> 输出只列出了 4 个核中第一个核的信息，从核数和缓存大小来看具体处理器型号估计是最大频率 1.1 GHz 的 <a href="https://ark.intel.com/content/www/us/en/ark/products/87461/intel-atom-x3-c3230rk-processor-1m-cache-up-to-1-10-ghz.html" target="_blank">x3-C3230RK</a>。</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 93
model name      : Genuine Intel(R) CPU         @ 728
stepping        : 1
microcode       : 0x102
cpu MHz         : 416.000
cache size      : 512 KB
physical id     : 0
siblings        : 4
core id         : 1
cpu cores       : 4
apicid          : 2
initial apicid  : 2
fdiv_bug        : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 11
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clflush mmx fxsr sse sse2 ss ht nx rdtscp lm constant_tsc arch_perfmon xtopology pni pclmulqdq monitor ssse3 cx16 xtpr pdcm sse4_1 sse4_2 movbe popcnt aes lahf_lm 3dnowprefetch tsc_adjust smep erms
bogomips        : 892.92
clflush size    : 64
cache_alignment : 64
address sizes   : 32 bits physical, 48 bits virtual
power management:
</pre>
<p>x3-C3230RK 处理器集成了 A-GOLD 620 射频收发器，支持 3G、Wi-Fi（802.11 B/G/N）、蓝牙 4.0、GPS 以及 FM 功能，这几项功能记录仪基本都用上了。GPU 则是 600 MHz 的 Mali-450 MP4。处理器最大支持 2 GB 内存，不过这款行车记录仪上只带了 1 GB 内存：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ cat /proc/meminfo
MemTotal:         948616 kB
MemFree:           26592 kB
MemAvailable:     498968 kB
Buffers:            3696 kB
Cached:           498580 kB
</pre>
<p>我们也可以使用 <code>dumpsys</code> 命令查询记录仪电池信息：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ dumpsys battery
Current Battery Service state:
  AC powered: false
  USB powered: true
  Wireless powered: false
  status: 2
  health: 2
  present: true
  level: 0
  scale: 100
  voltage: 3289
  temperature: 240
  technology: Li-ion
</pre>
<h2 id="system-app">系统 App</h2>
<p>上面的 Android 主界面里可以看到 Tong-server 和 VideoRecorderService 这两个服务在运行，分别对应记录仪的整体管理功能和录像功能，使用老版本系统的记录仪上可以打开 VideoServer 录像的系统 App 界面：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737372135/kuzo-video-server.png" alt="行车记录仪录像 App" title="行车记录仪录像 App"></p>
<p>对于 Vysor 不能打开图形界面的新系统可以用命令列举系统 App 列表（下面只列出了关键的几个）：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ pm list packages -f
package:/system/app/KuzoInstaller/KuzoInstaller.apk=com.hpyl.kuzo.installer
package:/system/app/ArcCamera/ArcCamera.apk=com.arcsoft.camera2
package:/system/app/KuzoLauncher/KuzoLauncher.apk=com.hpyl.kuzolauncher
package:/system/app/TongTermManager/TongTermManager.apk=com.kh.tong.termmanager
package:/system/app/VideoServer/VideoServer.apk=com.kh.videoserver
</pre>
<p>然后使用 <code>dumpsys</code> 命令查看相应 App 的信息：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ dumpsys package com.kh.tong.termmanager
  Package [com.kh.tong.termmanager] (24d3573f):
    userId=10016 gids=[3003, 1028, 1015, 1023, 3002, 3001]
    pkg=Package{c4b880c com.kh.tong.termmanager}
    codePath=/system/app/TongTermManager
    resourcePath=/system/app/TongTermManager
    legacyNativeLibraryDir=/system/app/TongTermManager/lib
    primaryCpuAbi=armeabi-v7a
    secondaryCpuAbi=null
    versionCode=1000027 targetSdk=23
    versionName=1.0.27
shell@Sf3gr_mrd6_p2_720:/ $ dumpsys package com.kh.videoserver
  Package [com.kh.videoserver] (31228436):
    userId=10018 gids=[3003, 1028, 1015, 1023]
    pkg=Package{27beef37 com.kh.videoserver}
    codePath=/system/app/VideoServer
    resourcePath=/system/app/VideoServer
    legacyNativeLibraryDir=/system/app/VideoServer/lib
    primaryCpuAbi=null
    secondaryCpuAbi=null
    versionCode=1000027 targetSdk=22
    versionName=1.0.27
</pre>
<p>可以看到新版本系统下 <code>Tong-server</code> 和 <code>VideoServer</code> App 都是 1.0.27 版本，如果把系统 App 的 apk 文件拷贝出来了也能用 <code>aapt</code> 命令查看：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ aapt dump badging TongTermManager.apk | grep &quot;VersionName&quot;
package: name='com.kh.tong.termmanager' versionCode='1000027' versionName='1.0.27' platformBuildVersionName='5.1.1-1819727'
(xenial)zzm@localhost:~$ aapt dump badging VideoServer.apk | grep &quot;VersionName&quot;
package: name='com.kh.videoserver' versionCode='1000027' versionName='1.0.27' platformBuildVersionName='5.1.1-1819727'
</pre>
<p>老版本系统下这两个 App 也是低一些的 1.0.23 版本：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ $ dumpsys package com.kh.tong.termmanager
  Package [com.kh.tong.termmanager] (2d37c5ca):
    userId=10017 gids=[3003, 1028, 1015, 1023, 3002, 3001]
    pkg=Package{33ef33b com.kh.tong.termmanager}
    codePath=/system/app/TongTermManager
    resourcePath=/system/app/TongTermManager
    legacyNativeLibraryDir=/system/app/TongTermManager/lib
    primaryCpuAbi=x86
    secondaryCpuAbi=null
    versionCode=1000023 targetSdk=22
    versionName=1.0.23
</pre>
<h2 id="mod-system">修改系统</h2>
<p>记录仪 Android AOSP 系统自带了 root 权限，如果需要修改系统，可以先关闭系统分区保护：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ adb disable-verity
Verity disabled on /system
Now reboot your device for settings to take effect
</pre>
<p>运行 <code>adb reboot</code> 命令重启记录仪之后，就可以直接 <code>su</code> 切换为 root 用户，重新挂载修改系统分区了：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ # mount -o remount,rw /dev/block/platform/soc0/e0000000.noc/by-name/ImcPartID068 /system
</pre>
<h2 id="update-system-app">尝试升级系统 App</h2>
<p>经过测试我发现 2.1.5 老版本系统相比新的 2.2.3 版本存在以下问题：</p>
<ul>
<li>音乐播放的暂停状态无法保存，记录仪重新开机时始终恢复播放上次的歌曲，这个时候只能使用蓝牙按键或者手机酷走 App 暂停播放；</li>
<li>记录仪录的视频画面中没有当前车速和时间 OSD 显示。</li>
</ul>
<p>因此我想着将 2.1.5 版本系统里的 <code>Tong-server</code> 和 <code>VideoServer</code> App 升级为 2.2.3 里自带的新版本，是不是就能解决这些问题。</p>
<p>不过由于这两个系统 App 的 apk 文件是不完整的，apk 中不包含 <code>classes.dex</code> 并且有单独的 odex 文件，这样的 apk 文件也无法直接安装，最好能先将 odex 反编译出来。</p>
<p>我先试了常用的 <a href="https://github.com/JesusFreke/smali" target="_blank">smali/baksmali</a> 来反编译 <code>VideoServer</code> 的 odex，注意 <code>baksmali</code> 更新之后命令的使用方式和网上很多文章不太一样了：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ java -jar baksmali-2.2.7.jar x VideoServer/x86/VideoServer.odex
Exception in thread &quot;main&quot; org.jf.dexlib2.DexFileFactory$UnsupportedOatVersionException: Unsupported oat version: 48
        at org.jf.dexlib2.DexFileFactory.loadDexFile(DexFileFactory.java:120)
        at org.jf.baksmali.DexInputCommand.loadDexFile(DexInputCommand.java:149)
        at org.jf.baksmali.DisassembleCommand.run(DisassembleCommand.java:162)
        at org.jf.baksmali.Main.main(Main.java:102)
</pre>
<p>额，原来 <code>baksmali</code> 并不支持比较老的 Android 5.1 版本。</p>
<p>看起来我只能使出 <a href="https://github.com/testwhat/SmaliEx" target="_blank">oat2dex</a> 工具来反编译试试了，<code>oat2dex</code> 使用上比 <code>baksmali</code> 复杂一些：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~/Downloads/tmp$ java -jar oat2dex.jar
Easy oat2dex 0.90
Usage:
 java -jar oat2dex.jar [options] &lt;action&gt;
[options]
 Api level: -a &lt;integer&gt;
 Output folder: -o &lt;folder path&gt;
 Print detail : -v
&lt;action&gt;
 Get dex of boot(.oat) : boot &lt;boot.oat/boot-folder&gt;
 Get dex (de-optimize) : &lt;oat/odex file&gt; &lt;boot-class-folder&gt;
                         &lt;vdex file&gt;
 Get raw odex          : odex &lt;oat/odex/vdex file&gt;
 Get raw odex smali    : smali &lt;oat/odex/vdex file&gt;
 Deodex framework (exp): devfw [empty or path of /system/framework/]
</pre>
<p>首先需要将新系统上的 <code>boot.oat</code> 文件拷贝出来：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ adb pull /data/dalvik-cache/x86/system@framework@boot.oat .
2063 KB/s (57823168 bytes in 27.362s)
</pre>
<p>使用 <code>oat2dex</code> 将 <code>boot.oat</code> 反优化为 dex 文件，默认会输出到当前目录下的 <code>boot.oat-dex</code> 子目录：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~/Downloads/tmp$ java -jar oat2dex.jar boot boot.oat
06-28 22:07:36:757 Art version=48 (boot.oat)
06-28 22:07:36:890 De-optimizing /system/framework/core-libart.jar
06-28 22:07:48:650 Output to /home/zzm/boot.oat-dex/core-libart.dex
06-28 22:07:48:651 De-optimizing /system/framework/conscrypt.jar
06-28 22:07:50:095 Output to /home/zzm/boot.oat-dex/conscrypt.dex
06-28 22:07:50:096 De-optimizing /system/framework/okhttp.jar
06-28 22:07:50:750 Output to /home/zzm/boot.oat-dex/okhttp.dex
06-28 22:07:50:751 De-optimizing /system/framework/core-junit.jar
06-28 22:07:50:801 Output to /home/zzm/boot.oat-dex/core-junit.dex
06-28 22:07:50:802 De-optimizing /system/framework/bouncycastle.jar
06-28 22:07:53:226 Output to /home/zzm/boot.oat-dex/bouncycastle.dex
06-28 22:07:53:228 De-optimizing /system/framework/ext.jar
06-28 22:07:55:777 Output to /home/zzm/boot.oat-dex/ext.dex
06-28 22:07:55:777 De-optimizing /system/framework/framework.jar
06-28 22:08:13:350 Output to /home/zzm/boot.oat-dex/framework.dex
06-28 22:08:13:351 De-optimizing /system/framework/framework.jar:classes2.dex
06-28 22:08:15:468 Analysis error in class=Landroid/widget/NumberPicker; method=&lt;init&gt;
Method: Landroid/widget/NumberPicker;-&gt;&lt;init&gt;(Landroid/content/Context;Landroid/util/AttributeSet;II)V
Near line: 604 (address 75)
Instructions: 
 [36] const/4 regA=15
 [37] move-object/from16 regA=0 regB=19
 [38] iput-boolean-quick regA=15 regB=0 &lt;-----
 [39] const/16 regA=15
 [40] const/16 regA=16

06-28 22:08:15:470 org.jf.dexlib2.analysis.AnalysisException: Could not resolve the field in class (Reference,Landroid/content/Context;) at offset 778 in &lt;init&gt;
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyzeIputIgetQuick(MethodAnalyzer.java:2422)
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyzeInstruction(MethodAnalyzer.java:1269)
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyze(MethodAnalyzer.java:259)
        at org.jf.dexlib2.analysis.MethodAnalyzer.&lt;init&gt;(MethodAnalyzer.java:190)
        at org.rh.smaliex.deopt.OdexRewriter$OdexRewriterModule$1$1.getInstructions(OdexRewriter.java:232)
        ...(Skip 24 traces)
        at org.rh.smaliex.OatUtil.convertToDex(OatUtil.java:271)
        at org.rh.smaliex.OatUtil.convertDexFromBootOat(OatUtil.java:192)
        at org.rh.smaliex.OatUtil.bootOat2Dex(OatUtil.java:75)
        at org.rh.smaliex.Main.mainImpl(Main.java:106)
        at org.rh.smaliex.Main.main(Main.java:43)

06-28 22:08:15:471 Failed to re-construct dex java.lang.ClassCastException: org.jf.dexlib2.analysis.UnresolvedOdexInstruction (in module: Unnamed Module) cannot be cast to org.jf.dexlib2.iface.instruction.formats.Instruction22cs (in module: Unnamed Module)
06-28 22:08:15:471 convertToDex: skip /system/framework/framework.jar:classes2.dex
06-28 22:08:15:472 De-optimizing /system/framework/telephony-common.jar
06-28 22:08:18:110 Output to /home/zzm/boot.oat-dex/telephony-common.dex
06-28 22:08:18:111 De-optimizing /system/framework/voip-common.jar
06-28 22:08:18:313 Output to /home/zzm/boot.oat-dex/voip-common.dex
06-28 22:08:18:314 De-optimizing /system/framework/ims-common.jar
06-28 22:08:18:503 Output to /home/zzm/boot.oat-dex/ims-common.dex
06-28 22:08:18:519 De-optimizing /system/framework/mms-common.jar
06-28 22:08:18:579 Output to /home/zzm/boot.oat-dex/mms-common.dex
06-28 22:08:18:599 De-optimizing /system/framework/android.policy.jar
06-28 22:08:19:007 Output to /home/zzm/boot.oat-dex/android.policy.dex
06-28 22:08:19:008 De-optimizing /system/framework/apache-xml.jar
06-28 22:08:20:770 Output to /home/zzm/boot.oat-dex/apache-xml.dex
</pre>
<p>处理 <code>boot.oat</code> 文件有些报错我们先忽略，接着我们可以用 <code>oat2dex</code> 反优化系统 App 的 odex 文件，这里用到了刚才 <code>boot.oat</code> 反优化出来的 dex 文件，类似的会输出到当前目录的子目录：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ java -jar oat2dex.jar VideoServer.odex boot.oat-dex/
06-28 22:13:09:212 Art version=48 (VideoServer.odex)
06-28 22:13:09:250 De-optimizing /system/app/VideoServer/VideoServer.apk
06-28 22:13:27:979 Output to /home/zzm/VideoServer.odex-dex/VideoServer.dex
</pre>
<p>反编译得到的 dex 文件，并重新编译成 <code>classes.dex</code>：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ java -jar baksmali-2.2.7.jar disassemble VideoServer.odex-dex/VideoServer.dex -o video
(xenial)zzm@localhost:~$ java -jar smali-2.2.7.jar assemble video -o classes.dex
</pre>
<p>然后将 <code>classes.dex</code> 打包添加到原来的 apk 文件中：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ aapt add VideoServer.apk classes.dex
</pre>
<p>安装新的 apk 文件存在报错：</p>
<pre class="brush: plain; title: ; notranslate">
W/PackageManager(  584): Package com.kh.videoserver signatures do not match the previously installed version; ignoring!
</pre>
<p>原来是没有签名，这里我使用 <a href="https://github.com/patrickfav/uber-apk-signer" target="_blank">Uber Apk Signer</a> 工具对 apk 进行重新签名：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ java -jar uber-apk-signer-1.0.0.jar -a VideoServer.apk
</pre>
<p>初步测试新编译出来的 <code>VideoServer</code> apk 可以正常安装启动，不过 <code>oat2dex</code> 处理 <code>Tong-server</code> 新版本 App 的时候就有问题了：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ java -jar oat2dex.jar TongTermManager.odex boot.oat-dex/
06-29 23:02:32:138 Art version=48 (TongTermManager.odex)
06-29 23:02:32:203 De-optimizing /system/app/TongTermManager/TongTermManager.apk
06-29 23:02:58:217 Output to /home/zzm/TongTermManager.odex-dex/TongTermManager.dex
06-29 23:02:58:217 De-optimizing /system/app/TongTermManager/TongTermManager.apk:classes2.dex
06-29 23:03:04:420 Analysis error in class=Lorg/jsoup/parser/Tag; method=&lt;clinit&gt;
Method: Lorg/jsoup/parser/Tag;-&gt;&lt;clinit&gt;()V
Near line: 295 (address 1223)
Instructions: 
 [624] check-cast regA=0
 [625] invoke-static regC=0
 [626] iput-boolean-quick regA=7 regB=0 &lt;-----
 [627] add-int/lit8 regA=3 regB=3
 [628] goto

06-29 23:03:04:433 org.jf.dexlib2.analysis.AnalysisException: Could not resolve the field in class (Reference,Lorg/jsoup/parser/Tag;) at offset 15 in &lt;clinit&gt;
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyzeIputIgetQuick(MethodAnalyzer.java:2422)
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyzeInstruction(MethodAnalyzer.java:1269)
        at org.jf.dexlib2.analysis.MethodAnalyzer.analyze(MethodAnalyzer.java:259)
        at org.jf.dexlib2.analysis.MethodAnalyzer.&lt;init&gt;(MethodAnalyzer.java:190)
        at org.rh.smaliex.deopt.OdexRewriter$OdexRewriterModule$1$1.getInstructions(OdexRewriter.java:232)
        ...(Skip 24 traces)
        at org.rh.smaliex.OatUtil.convertToDex(OatUtil.java:271)
        at org.rh.smaliex.OatUtil.convertDexFromBootOat(OatUtil.java:192)
        at org.rh.smaliex.OatUtil.oat2dex(OatUtil.java:81)
        at org.rh.smaliex.Main.mainImpl(Main.java:127)
        at org.rh.smaliex.Main.main(Main.java:43)

06-29 23:03:04:437 Failed to re-construct dex java.lang.ClassCastException: org.jf.dexlib2.analysis.UnresolvedOdexInstruction (in module: Unnamed Module) cannot be cast to org.jf.dexlib2.iface.instruction.formats.Instruction22cs (in module: Unnamed Module)
06-29 23:03:04:437 convertToDex: skip /system/app/TongTermManager/TongTermManager.apk:classes2.dex
</pre>
<p>这个问题目前还没有找到好的解决方法，由于担心整个系统升级也会碰到 Vysor 主界面无法显示的问题，而且酷走记录仪的生产厂家也不提供支持没有单独的升级包，老版本系统的管理和录像 App 也就继续使用了。</p>
<h2 id="mod-app-config">修改系统 App 配置</h2>
<h3 id="tong-server">Tong-server</h3>
<p>前面说的 2.1.5 版本系统音乐播放的暂停状态问题，可以看看 <code>Tong-server</code> 系统 App 的配置文件 <code>/data/data/com.kh.tong.termmanager/shared_prefs/KUZOConfig.xml</code>，例如我的记录仪配置：</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;
&lt;map&gt;
    &lt;string name=&quot;LOCAL_SAVE_MUSIC_INFO_KEY2F06A8D7CA314388B58DC46719702844&quot;&gt;{&amp;quot;des&amp;quot;:null,&amp;quot;id&amp;quot;:0,&amp;quot;md5&amp;quot;:&amp;quot;bdb368fe-b568-4157-9b17-818104295f57&amp;quot;,&amp;quot;seekProgress&amp;quot;:5418,&amp;quot;title&amp;quot;:null,&amp;quot;url&amp;quot;:&amp;quot;/storage/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/15/15/bdb368fe-b568-4157-9b17-818104295f57.mp3&amp;quot;}&lt;/string&gt;
    &lt;string name=&quot;USER_TERM_MUSIC_LIST_KEY&quot;&gt;[{&amp;quot;fileSize&amp;quot;:4008624,&amp;quot;id&amp;quot;:0,&amp;quot;length&amp;quot;:0,&amp;quot;md5&amp;quot;:&amp;quot;bdb368fe-b568-4157-9b17-818104295f57&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;?? ?? - ?????&amp;quot;,&amp;quot;path&amp;quot;:&amp;quot;/storage/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/0/0/bdb368fe-b568-4157-9b17-818104295f57.mp3&amp;quot;,&amp;quot;singer&amp;quot;:null,&amp;quot;userUuid&amp;quot;:&amp;quot;2F06A8D7CA314388B58DC46719702844&amp;quot;,&amp;quot;uuid&amp;quot;:&amp;quot;bdb368fe-b568-4157-9b17-818104295f57&amp;quot;},{&amp;quot;fileSize&amp;quot;:6749379,&amp;quot;id&amp;quot;:0,&amp;quot;length&amp;quot;:0,&amp;quot;md5&amp;quot;:&amp;quot;b49b55b0-ae7c-47d5-9cb4-6cf03249efdc&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;????? - ???&amp;quot;,&amp;quot;path&amp;quot;:&amp;quot;/storage/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/1/0/b49b55b0-ae7c-47d5-9cb4-6cf03249efdc.mp3&amp;quot;,&amp;quot;singer&amp;quot;:null,&amp;quot;userUuid&amp;quot;:&amp;quot;2F06A8D7CA314388B58DC46719702844&amp;quot;,&amp;quot;uuid&amp;quot;:&amp;quot;b49b55b0-ae7c-47d5-9cb4-6cf03249efdc&amp;quot;}]&lt;/string&gt;
    &lt;string name=&quot;userUUID&quot;&gt;2F06A8D7CA314388B58DC46719702844&lt;/string&gt;
    &lt;string name=&quot;LOCAL_SAVE_RADIO_INFO_KEY2F06A8D7CA314388B58DC46719702844&quot;&gt;{&amp;quot;coverUrlLarge&amp;quot;:null,&amp;quot;coverUrlSmall&amp;quot;:null,&amp;quot;id&amp;quot;:-1,&amp;quot;isCollected&amp;quot;:false,&amp;quot;kind&amp;quot;:null,&amp;quot;programName&amp;quot;:null,&amp;quot;radioDesc&amp;quot;:null,&amp;quot;radioName&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;radioPlayCount&amp;quot;:null,&amp;quot;rate24AacUrl&amp;quot;:&amp;quot;http://live.xmcdn.com/live/536/24.m3u8&amp;quot;,&amp;quot;rate24TsUrl&amp;quot;:&amp;quot;http://live.xmcdn.com/live/536/24.m3u8&amp;quot;,&amp;quot;rate64AacUrl&amp;quot;:&amp;quot;http://live.xmcdn.com/live/536/24.m3u8&amp;quot;,&amp;quot;rate64TsUrl&amp;quot;:&amp;quot;http://live.xmcdn.com/live/536/24.m3u8&amp;quot;,&amp;quot;scheduleID&amp;quot;:null,&amp;quot;supportBitRates&amp;quot;:null,&amp;quot;updatedAt&amp;quot;:null}&lt;/string&gt;
    &lt;boolean name=&quot;LOCAL_SAVE_MUSIC_IS_PAUSE_KEY&quot; value=&quot;false&quot; /&gt;
    &lt;string name=&quot;USER_TERM_TONG_SESSION_LIST_KEY&quot;&gt;[]&lt;/string&gt;
    &lt;int name=&quot;com.kh.tong.term.statemachine.TermMenuSM.LAST_SAVED_MENU_KEY&quot; value=&quot;0&quot; /&gt;
    &lt;int name=&quot;com.kh.tong.term.statemachine.TermAudioVolumeSM.PrefKeyVolumn&quot; value=&quot;7&quot; /&gt;
    &lt;boolean name=&quot;LOCAL_SAVE_RADIO_IS_PAUSE_KEY&quot; value=&quot;false&quot; /&gt;
    &lt;string name=&quot;Password&quot;&gt;8888&lt;/string&gt;
&lt;/map&gt;
</pre>
<p>主要配置项如下：</p>
<ul>
<li><code>userUUID</code> 应该是酷走 App 的用户 ID；</li>
<li><code>USER_TERM_MUSIC_LIST_KEY</code> 是通过酷走 App 收藏上传的歌曲列表；</li>
<li><code>LOCAL_SAVE_MUSIC_INFO_KEYxxx</code> 是当前用户的歌曲播放信息；</li>
<li><code>LOCAL_SAVE_RADIO_INFO_KEYxxx</code> 是通过酷走 App 收藏添加的在线电台列表；</li>
<li><code>LOCAL_SAVE_MUSIC_IS_PAUSE_KEY</code> 表示保存的音乐播放暂停状态；</li>
<li><code>LOCAL_SAVE_RADIO_IS_PAUSE_KEY</code> 表示保存的电台播放暂停状态；</li>
<li><code>com.kh.tong.term.statemachine.TermMenuSM.LAST_SAVED_MENU_KEY</code> 表示保存的菜单键状态（0：音乐，1：点播，2：电台）；</li>
<li><code>com.kh.tong.term.statemachine.TermAudioVolumeSM.PrefKeyVolumn</code> 为系统音量值；</li>
<li><code>Password</code> 为记录仪的管理密码。</li>
</ul>
<p>对于老版本系统音乐播放的暂停状态得不到保存的问题，可以修改系统脚本强制设为暂停状态，需要播放音乐时通过蓝牙按键启动，我是在 <code>/system/bin/load_iwlwifi.sh</code> 文件后附加操作命令的：</p>
<pre class="brush: bash; title: ; notranslate">
shell@Sf3gr_mrd6_p2_720:/ # cat &gt;&gt; /system/bin/load_iwlwifi.sh
KUZO_CFG=&quot;/data/data/com.kh.tong.termmanager/shared_prefs/KUZOConfig.xml&quot;

busybox sed -i '/LOCAL_SAVE_MUSIC_IS_PAUSE_KEY/s/value=&quot;.*&quot;/value=&quot;true&quot;/' $KUZO_CFG
busybox sed -i '/LOCAL_SAVE_RADIO_IS_PAUSE_KEY/s/value=&quot;.*&quot;/value=&quot;true&quot;/' $KUZO_CFG
</pre>
<h3 id="videoserver">VideoServer</h3>
<p>录像 App 的配置文件 <code>/data/data/com.kh.videoserver/shared_prefs/com.kh.videoserver_preferences.xml</code> 比较简单：</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;
&lt;map&gt;
    &lt;string name=&quot;rtsp_port&quot;&gt;8886&lt;/string&gt;
&lt;/map&gt;
</pre>
<p>可以通过 <code>rtsp_port</code> 参数修改视频服务器的 RTSP 端口号。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kuzo-android-internal/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>初探酷走Android行车记录仪</title>
		<link>https://zohead.com/archives/kuzo-recorder/</link>
		<comments>https://zohead.com/archives/kuzo-recorder/#comments</comments>
		<pubDate>Tue, 30 Oct 2018 15:44:55 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[RTSP]]></category>
		<category><![CDATA[行车记录仪]]></category>
		<category><![CDATA[酷走]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1554</guid>
		<description><![CDATA[酷走记录仪 博客有段时间没有更新了，提前说好这不是一般的行车记录仪评测文章，我现在开的 SUV 上并没有装记录仪，只是同事刚好送了一个厂商已倒闭的记录仪，看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣，准备初步研究下。 酷走行车记录仪由深圳汉普云联科技生产，具体型号为 KZV201，网上关于此行车记录仪的评测文章还是有一些的，之前 京东众筹 上的链接应该还在，想了解的朋友们可以看看。 这里我就不做具体介绍了，外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器，因此集成了 3G 上网功能（带 SIM 卡插槽），支持 2.4 GHz Wi-Fi  [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="kuzo-recorder">酷走记录仪</h2>
<p>博客有段时间没有更新了，提前说好这不是一般的行车记录仪评测文章，我现在开的 SUV 上并没有装记录仪，只是同事刚好送了一个厂商已倒闭的记录仪，看到这款采用 Intel Atom 处理器的行车记录仪有点兴趣，准备初步研究下。</p>
<p>酷走行车记录仪由深圳汉普云联科技生产，具体型号为 KZV201，网上关于此行车记录仪的评测文章还是有一些的，之前 <a href="http://z.jd.com/project/details/42623.html" target="_blank">京东众筹</a> 上的链接应该还在，想了解的朋友们可以看看。</p>
<p>这里我就不做具体介绍了，外观图也就不上了。初步了解此记录仪使用 Intel SOFIA Atom x3 处理器，因此集成了 3G 上网功能（带 SIM 卡插槽），支持 2.4 GHz Wi-Fi 网络、蓝牙 4.0、GPS 及 FM 发射功能。酷走记录仪虽然号称支持 1080p 全高清视频录制，然而实际测试录像效果也是比较一般，另外采用 Android 5.0 系统也挺少见。</p>
<p>由于机身没有任何物理按键，所有功能都要通过手机 App 连接行车记录仪 Wi-Fi 热点来完成，这也是相当坑的地方：由于厂家已经不提供支持更新了，目前手机 App 里不少功能缺失，最基本的 SIM 卡（专用物联网卡）数据流量充值功能都不能使用；手机 App 连修改记录仪系统时间的功能都没有提供（可能厂家考虑的是自动通过 SIM 卡数据流量进行时间同步），导致目前记录仪的时间都不正确。</p>
<h2 id="web-api">Web 接口使用</h2>
<p>为了摆脱随时可能会完全崩掉的手机 App，我就需要知道基本的管理接口和视频调阅接口。</p>
<p>记录仪自带的 Wi-Fi 热点使用固定的 <code>192.168.43.1</code> IP 地址（别指望能修改了），手机或者电脑连接酷走的 Wi-Fi 热点之后就可以 ping 通记录仪地址了，下面我贴出来的例子都是在 Chromebook 上测试的，首先我用 Linux 自带的 <code>nc</code> 命令来扫一下记录仪开放的端口：</p>
<pre class="brush: bash; title: ; notranslate">
(xenial)zzm@localhost:~$ nc -znv 192.168.43.1 20-20000 2&gt;&amp;1 | grep 'succeeded'
Connection to 192.168.43.1 53 port [tcp/*] succeeded!
Connection to 192.168.43.1 5556 port [tcp/*] succeeded!
Connection to 192.168.43.1 8080 port [tcp/*] succeeded!
Connection to 192.168.43.1 8886 port [tcp/*] succeeded!
</pre>
<p>显然 <code>53</code> 是 Wi-Fi 热点自带的 DNS 服务器端口，看起来 <code>8080</code> 就是 Web 接口的端口了，我在安装了酷走 App 的手机上运行 tcpdump 程序进行抓包就可以分析 8080 端口的请求了。</p>
<h3 id="用户登录">用户登录</h3>
<p>首先是用户登录请求：</p>
<pre class="brush: bash; title: ; notranslate">
chronos@localhost ~/Downloads $ curl -v &quot;http://192.168.43.1:8080/term?act=user_login&amp;user=\{%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22\}&amp;password=8888&amp;termtype=3&amp;force=false&quot;
*   Trying 192.168.43.1...
* TCP_NODELAY set
* Connected to 192.168.43.1 (192.168.43.1) port 8080 (#0)
&gt; GET /term?act=user_login&amp;user={%22email%22:null,%22emailVerifyCode%22:null,%22emailVerifyCodeValidtime%22:null,%22emailVerifyStatus%22:0,%22iconUuid%22:null,%22id%22:0,%22lastLoginTime%22:null,%22name%22:null,%22nickname%22:null,%22password%22:null,%22phone%22:null,%22phoneLoginCode%22:null,%22phoneLoginCodeValidtime%22:null,%22realName%22:null,%22regTime%22:null,%22uuid%22:%222F06A8D7CA314388B58DC46719702844%22}&amp;password=8888&amp;termtype=3&amp;force=false HTTP/1.1
&gt; Host: 192.168.43.1:8080
&gt; User-Agent: curl/7.60.0
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Set-Cookie: JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi;Path=/
&lt; Set-Cookie: TONG_TOKEN_ID=a2664fc77f2d4c2abf90bea58e3bb6bf;Path=/;Expires=Tue, 29-Feb-2000 00:26:30 GMT
&lt; Expires: Thu, 01 Jan 1970 00:00:00 GMT
&lt; Content-Type: text/plain;charset=UTF-8
&lt; Access-Control-Allow-Methods: GET, POST
&lt; Access-Control-Allow-Credentials: true
&lt; Transfer-Encoding: chunked
&lt; Server: Jetty(i-jetty 1.0.27)
&lt; 
* Connection #0 to host 192.168.43.1 left intact
{&quot;data&quot;:&quot;a2664fc77f2d4c2abf90bea58e3bb6bf&quot;,&quot;elapsedTime&quot;:94,&quot;message&quot;:&quot;鉴权通过!&quot;,&quot;responseCode&quot;:100}
</pre>
<blockquote>
<p><strong>注意</strong></p>
<p>上面 curl 命令中的括号做了转义防止 curl 请求错误，实际请求地址中并没有反斜杠，另外请求地址中的 <code>%22</code> 就是双引号，如果在浏览器中访问也可以直接把 <code>%22</code> 换成双引号。</p>
</blockquote>
<p>可以看到记录仪 Web 服务器是基于 Jetty 写的，GET 请求地址中的 <code>user</code> 和 <code>password</code> 参数不能缺少，<code>password</code> 就是记录仪的管理密码（默认：8888），<code>user</code> 参数是个 JSON 对象，基本所有参数都可以使用 <code>null</code>，除了 <code>uuid</code> 参数表示登录用户（可以随机生成一串 UUID），重复发送登录请求将会报错。</p>
<p>最值得关注的是返回 Cookie 数据中的 <code>JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi</code> 值，后面所有 Web 请求都会用到这个会话 ID。</p>
<h3 id="device-info">查看设备信息</h3>
<p>基础设备信息也可以使用 curl 附带 <code>JSESSIONID</code> Cookie 值进行查询，下面其它的请求就不详细列出来了：</p>
<pre class="brush: bash; title: ; notranslate">
curl -v -b &quot;JSESSIONID=1lqydpb54hnunwlfbfj8eqkzi&quot; &quot;http://192.168.43.1:8080/term?act=device_info&amp;appID=null&quot;
</pre>
<p>设备信息输出如下：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=device_info&amp;appID=null HTTP/1.1
Host: 192.168.43.1:8080
Cookie: JSESSIONID=73jb4a4t1urf1un1krkwn6drr

HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Credentials: true
Transfer-Encoding: chunked
Server: Jetty(i-jetty 1.0.27)

{&quot;data&quot;:{&quot;hotName&quot;:null,&quot;hotPassword&quot;:null,&quot;hwVerion&quot;:&quot;4&quot;,&quot;imei&quot;:&quot;XXXXXXX&quot;,&quot;osVersion&quot;:&quot;2.2.3&quot;,&quot;password&quot;:null,&quot;productCode&quot;:null,&quot;randomCode&quot;:null,&quot;sn&quot;:&quot;KXXXXXX&quot;,&quot;swVersion&quot;:&quot;1.0.27&quot;,&quot;videoPassword&quot;:null},&quot;elapsedTime&quot;:9,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>查看记录仪配置请求：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_termconfiginfo HTTP/1.1

{&quot;data&quot;:{&quot;hotName&quot;:&quot;KUZO_KXXXXXX&quot;,&quot;hotPassword&quot;:&quot;password&quot;,&quot;hwVerion&quot;:&quot;4&quot;,&quot;imei&quot;:&quot;XXXXXXX&quot;,&quot;osVersion&quot;:&quot;2.2.3&quot;,&quot;password&quot;:&quot;8888&quot;,&quot;productCode&quot;:null,&quot;randomCode&quot;:null,&quot;sn&quot;:&quot;KXXXXXX&quot;,&quot;swVersion&quot;:&quot;1.0.27&quot;,&quot;videoPassword&quot;:&quot;&quot;},&quot;elapsedTime&quot;:14,&quot;message&quot;:&quot;Success!&quot;,&quot;responseCode&quot;:100}
</pre>
<p>查看 SIM 卡 ICCID（返回值在 <code>data</code> 中）：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_sim_iccid HTTP/1.1

{&quot;data&quot;:&quot;XXXXXXX&quot;,&quot;elapsedTime&quot;:14,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>查看 FM 发射状态（这个 POST 请求没有附带任何参数）：</p>
<pre class="brush: plain; title: ; notranslate">
POST /term?act=query_fm_open_status HTTP/1.1
Content-Length: 0

{&quot;data&quot;:false,&quot;elapsedTime&quot;:35,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>查看 GPS 状态：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_gps_status HTTP/1.1

{&quot;data&quot;:{&quot;position&quot;:{&quot;accuracy&quot;:14.33,&quot;altitude&quot;:15.030368,&quot;angle&quot;:337.0591,&quot;crs&quot;:1,&quot;gpsTime&quot;:&quot;20181023180046&quot;,&quot;latitude&quot;:31.907135402870722,&quot;longitude&quot;:118.77761111400555,&quot;speed&quot;:0.0},&quot;positionFix&quot;:true,&quot;positionType&quot;:1,&quot;satellitesInUse&quot;:5,&quot;satellitesInView&quot;:6},&quot;elapsedTime&quot;:3,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>可以看到这里能直接查询到 GPS 的当前经纬度以及精确度，还能看到卫星的使用情况，比较讽刺的是这边 GPS 时间都得到了，记录仪系统在网络不可用的情况下却没考虑使用 GPS 时间。</p>
<p>查询存储状态（装了 TF 卡之后就能看到存储卡容量状态了）：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_storage_info HTTP/1.1

{&quot;data&quot;:{&quot;TFExist&quot;:true,&quot;TFTotalCapacity&quot;:63847890944,&quot;TFUsedCapacity&quot;:6595575808,&quot;internalTotalCapacity&quot;:3918114816,&quot;internalUsedCapacity&quot;:2259316736},&quot;elapsedTime&quot;:3,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>查询 3G 移动网络状态：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_3g_status HTTP/1.1

{&quot;data&quot;:{&quot;dialUpSuccess&quot;:false,&quot;intensity&quot;:0,&quot;mnc&quot;:&quot;01&quot;,&quot;type&quot;:&quot;3G&quot;},&quot;elapsedTime&quot;:19,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<h3 id="music-api">音乐管理</h3>
<p>此记录仪支持通过手机 App 上传音乐到记录仪的存储卡，然后通过蓝牙进行播放控制，列举音乐接口：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=list_music HTTP/1.1

{&quot;data&quot;:[{&quot;fileSize&quot;:4008624,&quot;id&quot;:0,&quot;length&quot;:0,&quot;md5&quot;:&quot;e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37&quot;,&quot;name&quot;:null,&quot;path&quot;:&quot;/mnt/media_rw/sdcard1/Kuzo/Music/2F06A8D7CA314388B58DC46719702844/2/13/e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37.mp3&quot;,&quot;singer&quot;:null,&quot;userUuid&quot;:&quot;2F06A8D7CA314388B58DC46719702844&quot;,&quot;uuid&quot;:&quot;e8b1a4e5-0a4d-4c1f-b6bf-44052507fc37&quot;}],&quot;elapsedTime&quot;:7,&quot;message&quot;:&quot;Success!&quot;,&quot;responseCode&quot;:100}
</pre>
<p>查询最大音量（最大级别为 15）：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_max_volume HTTP/1.1

{&quot;data&quot;:15,&quot;elapsedTime&quot;:5,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>查看当前音量：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_volume HTTP/1.1

{&quot;data&quot;:10,&quot;elapsedTime&quot;:9,&quot;message&quot;:&quot;15 15&quot;,&quot;responseCode&quot;:100}
</pre>
<h3 id="video-api">视频管理</h3>
<p>首先按小时段列举视频，请忽略不正确的系统时间：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=list_video_hour HTTP/1.1

{&quot;data&quot;:[&quot;20000115230000&quot;,&quot;20000116000000&quot;,&quot;20000116010000&quot;,&quot;20000116020000&quot;,&quot;20000116030000&quot;],&quot;elapsedTime&quot;:171,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>指定开始和结束的小时时间段列举视频：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=list_poi&amp;start_time=20000116030000&amp;end_time=20000116040000 HTTP/1.1

{&quot;data&quot;:[],&quot;elapsedTime&quot;:9,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>列举某个小时的所有视频，可以看到存储卡里视频的完整路径：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=list_video_db&amp;hour_time=20000116030000 HTTP/1.1

{&quot;data&quot;:[{&quot;createTime&quot;:&quot;20000116030127&quot;,&quot;id&quot;:42,&quot;length&quot;:120009,&quot;lockType&quot;:0,&quot;name&quot;:&quot;2000-01-16_03-01-27.mp4&quot;,&quot;path&quot;:&quot;/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-01-27.mp4&quot;,&quot;sn&quot;:null,&quot;uuid&quot;:&quot;a6035028ded14482936c1580aba8e298&quot;,&quot;videoType&quot;:0},{&quot;createTime&quot;:&quot;20000116030327&quot;,&quot;id&quot;:43,&quot;length&quot;:119940,&quot;lockType&quot;:0,&quot;name&quot;:&quot;2000-01-16_03-03-27.mp4&quot;,&quot;path&quot;:&quot;/mnt/media_rw/sdcard1/Kuzo/Video/2000/1/16/3/2000-01-16_03-03-27.mp4&quot;,&quot;sn&quot;:null,&quot;uuid&quot;:&quot;291a4796c1274dfea99a978c84ca33d0&quot;,&quot;videoType&quot;:0}],&quot;elapsedTime&quot;:986,&quot;message&quot;:null,&quot;responseCode&quot;:100}
</pre>
<p>获取某个视频文件的封面截图，此请求直接返回 jpg 图像数据：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=snapshot&amp;name=2000-01-16_03-21-26.mp4&amp;time=0 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: image/jpeg
Accept-Ranges: bytes
Content-Length: 42351
Content-Disposition: attachment;filename=2000-01-16_03-19-26_0.jpg
Server: Jetty(i-jetty 1.0.27)
</pre>
<p>指定开始和结束时间段获取历史轨迹，由于记录仪的 GPS 是一直开启的，还好轨迹还算比较准确，这里轨迹数据比较长我就不详细贴出来了：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=query_history_track&amp;start_time=20181013180104&amp;end_time=20181013180304&amp;len=121 HTTP/1.1
</pre>
<p>最后是最关键的下载录像接口，通过 <code>name</code> 参数指定视频文件名即可：</p>
<pre class="brush: plain; title: ; notranslate">
GET /term?act=get_video&amp;name=2000-01-16_03-19-26.mp4 HTTP/1.1
User-Agent: Lavf/57.56.100
Accept: */*
Range: bytes=0-
Connection: close
Host: 192.168.43.1:8080
Icy-MetaData: 1

HTTP/1.1 206 Partial Content
Content-Type: application/octet-stream
Accept-Ranges: bytes
Content-Length: 61722040
Content-Disposition: attachment;filename=2000-01-16_03-19-26.mp4
Content-Range: bytes 3104-61722039/61722040
Connection: close
Server: Jetty(i-jetty 1.0.27)
</pre>
<p>有点奇葩的就是这个下载录像的请求却并没有验证用户会话 ID，因此你也可以通过任何一款播放器指定地址直接远程回放录像：</p>
<pre>http://192.168.43.1:8080/term?act=get_video&#038;name=2000-01-16_03-19-26.mp4</pre>
<h2 id="live-video-api">实时视频接口</h2>
<p>通过 tcpdump 抓包就会发现记录仪的实时视频预览是通过上面扫描出来的 8886 端口，记录仪提供 RTSP 形式的实时预览接口：</p>
<pre class="brush: plain; title: ; notranslate">
rtsp://192.168.43.1:8886?videoapi=mc2&amp;transport=tcp
</pre>
<p>需要注意的是记录仪自带的 RTSP 服务器似乎只支持 TCP 流传输，如果使用 VLC 播放器，则需要将 Live555 流传输选项改为：RTP over RTSP (TCP) 才能正常播放。</p>
<p>厂家可能出于预览流畅性考虑 RTSP 并没有直接提供 1080p 的视频，然而我通过 VLC 播放器进行 RTSP 实时预览还是存在两至三秒的延时，在 Chromebook 下切换使用 <a href="https://chrome.google.com/webstore/detail/vxg-media-player/hncknjnnbahamgpjoafdebabmoamcnni" target="_blank">VXG Media Player</a> 插件进行实时预览则不存在延时问题。</p>
<h2 id="summarize">总结</h2>
<p>目前该行车记录仪使用下来还存在几个主要问题：</p>
<ul>
<li>SIM 卡无法联网的情况下系统时间没办法修改；</li>
<li>手机 App 提供的记录仪通过扫码连接外部 Wi-Fi 或者手机热点的功能无法工作，如果可以的话系统时间同步应该就不是问题；</li>
<li>记录仪硬件上有降噪麦克风，而且支持通过蓝牙的语音对讲功能（只用于目前手机 App 里基本不可用的结伴出行组队），但是视频录像却不支持音频录制，同样 RTSP 实时视频中也没有带音频流。</li>
</ul>
<p>这些问题只能后面有空的话再抽时间看看能否进入记录仪的 Android 系统进行修改了，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kuzo-recorder/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Android使用Shell命令配合Tasker控制手机</title>
		<link>https://zohead.com/archives/tasker-shell/</link>
		<comments>https://zohead.com/archives/tasker-shell/#comments</comments>
		<pubDate>Tue, 13 Dec 2016 16:38:02 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[GPS]]></category>
		<category><![CDATA[NFC]]></category>
		<category><![CDATA[Secure Settings]]></category>
		<category><![CDATA[Shadowsocks]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[Tasker]]></category>
		<category><![CDATA[飞行模式]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1325</guid>
		<description><![CDATA[Secure Settings 对比 Shell 命令 去年我写过一篇 Android 使用 Trigger + Tasker 设定 NFC 标签 的文章，主要介绍怎么使用 Tasker 和 Trigger App 设定 Android 手机刷 NFC 标签时执行指定的操作。该文章中对于手机的 飞行模式 / GPS / NFC 等功能的控制操作都是通过 Secure Settings App 实现的。 不过可惜的是我的华硕 Zenfone2 手机 ROM 升级到 6.0 版本之后，Secure Settings App 就有很多功能不能正常工作了，而且由于开发者也基本不再维护该 App 也有差 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="secure-settings-对比-shell-命令">Secure Settings 对比 Shell 命令</h2>
<p>去年我写过一篇 <a href="https://zohead.com/archives/tasker-nfc/">Android 使用 Trigger + Tasker 设定 NFC 标签</a> 的文章，主要介绍怎么使用 <a href="https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm" target="_blank">Tasker</a> 和 <a href="https://play.google.com/store/apps/details?id=com.jwsoft.nfcactionlauncher" target="_blank">Trigger</a> App 设定 Android 手机刷 NFC 标签时执行指定的操作。该文章中对于手机的 飞行模式 / GPS / NFC 等功能的控制操作都是通过 <a href="https://play.google.com/store/apps/details?id=com.intangibleobject.securesettings.plugin" target="_blank">Secure Settings</a> App 实现的。</p>
<p>不过可惜的是我的华硕 Zenfone2 手机 ROM 升级到 6.0 版本之后，Secure Settings App 就有很多功能不能正常工作了，而且由于开发者也基本不再维护该 App 也有差不多两年没有任何更新。</p>
<p>最近看到 <a href="http://www.notenoughtech.com/tasker/tasker-run-shell-commands/" target="_blank">Best run shell commands in Tasker</a> 文章介绍  Tasker 的几个常用 Shell 命令控制移动数据、飞行模式等选项，使用 Tasker 自带的运行 Shell 命令功能相比 Secure Settings 插件也有一些好处：</p>
<ul>
<li>Tasker 直接支持运行 Shell 命令（新增 <strong>操作类别</strong> -&gt; <strong>代码</strong> -&gt; <strong>运行外壳</strong> 即可），不需要专门安装 Secure Settings 等第三方 App；</li>
<li>多数命令在手机 root 后就能正常工作，不像 Secure Settings 那样需要特别安装成系统 App；</li>
<li>Shell 命令失效需要替换时也比较灵活，直接改 Shell 命令行就可以。</li>
</ul>
<p>因此我还是卸载了 Secure Settings App，将原来文章里的所有 Tasker 任务的 Secure Settings 操作都以 Shell 命令进行替代。</p>
<h2 id="tasker-任务使用-shell-命令">Tasker 任务使用 Shell 命令</h2>
<h3 id="数据开关-运行-shadowsocks">数据开关 + 运行 Shadowsocks</h3>
<p>为实现以下目标：</p>
<ol>
<li>具有路由器翻墙的住处网络环境下自动关闭手机 Shadowsocks 和移动数据；</li>
<li>其它网络环境下都开启 Shadowsocks 手机翻墙；</li>
<li>任何 Wi-Fi 环境下关闭移动数据以省电；</li>
<li>离开任何 Wi-Fi 自动开启移动数据。</li>
</ol>
<p>为此我建了 4 个 Tasker 配置和任务（当然也可以更简单）：</p>
<ol>
<li>连接住处的 OpenWRT Wi-Fi（根据 Tasker 的 SSID 条件区分） <br />
关闭 Shadowsocks 和移动数据；</li>
<li>连接其它 Wi-Fi <br />
关闭移动数据；</li>
<li>离开任何 Wi-Fi <br />
开启移动数据；</li>
<li>离开住处 Wi-Fi <br />
开启 Shadowsocks。</li>
</ol>
<p>现在 Android 版本 Shadowsocks App 是自带 Tasker 插件支持的（操作类别里选 <strong>插件</strong> -&gt; <strong>影梭</strong> 即可），开启和关闭 Shadowsocks 配置非常简单。</p>
<p>移动数据开关可以分别用下面的命令（Tasker 运行外壳中需要启用 <strong>使用Root</strong> 选项）：</p>
<pre class="brush: bash; title: ; notranslate">
svc data enable
svc data disable
</pre>
<h3 id="控制飞行模式及-nfc">控制飞行模式及 NFC</h3>
<p>为了实现和之前文章里类似的目标：</p>
<ol>
<li>睡觉前刷 NFC 标签开启飞行模式；</li>
<li>打开 NFC（这样起来之后可以刷 NFC 标签）；</li>
<li>起来刷 NFC 标签关闭飞行模式。</li>
</ol>
<p>另外考虑最好只使用一个 NFC 标签就达到效果，这里我只建了一个 Tasker 任务包含 4 个操作：</p>
<ol>
<li>Tasker 操作类别中选 <strong>变量</strong> -&gt; <strong>变量设置</strong>，<code>%TMPAIR</code> 发往 <code>%AIR</code> <br />
先保存 <code>%AIR</code> 这个 Tasker 自带的飞行模式变量；</li>
<li>如果 <code>%AIR = off</code> 则运行外壳开启飞行模式；</li>
<li>如果 <code>%AIR = on</code> 则运行外壳开启 NFC；</li>
<li>如果 <code>%TMPAIR = on</code> 则运行外壳关闭飞行模式。</li>
</ol>
<h4 id="开启关闭飞行模式">开启关闭飞行模式</h4>
<p>开启和关闭飞行模式可以直接使用下面的两条命令：</p>
<pre class="brush: bash; title: ; notranslate">
settings put global airplane_mode_on 1; am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true
settings put global airplane_mode_on 0; am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false
</pre>
<h4 id="开启关闭-nfc">开启关闭 NFC</h4>
<p>开启和关闭 NFC 功能则相对复杂一些，需要区分 Android 系统版本：</p>
<ul>
<li>Android 4.4 以上：</li>
</ul>
<pre class="brush: bash; title: ; notranslate">
# Disable NFC
service call nfc 5
# Enable NFC
service call nfc 6
</pre>
<ul>
<li>Android 4.0.1 - Android 4.3.1：</li>
</ul>
<pre class="brush: bash; title: ; notranslate">
# Disable NFC
service call nfc 4
# Enable NFC
service call nfc 5
</pre>
<ul>
<li>Android 2.3.4 - Android 2.3.7：</li>
</ul>
<pre class="brush: bash; title: ; notranslate">
# Disable NFC
service call nfc 18
# Enable NFC
service call nfc 19
</pre>
<ul>
<li>Android 2.3.3：</li>
</ul>
<pre class="brush: bash; title: ; notranslate">
# Disable NFC
service call nfc 20
# Enable NFC
service call nfc 21
</pre>
<ul>
<li>Android 2.3.2：</li>
</ul>
<pre class="brush: bash; title: ; notranslate">
# Disable NFC
service call nfc 13
# Enable NFC
service call nfc 14
</pre>
<p>我在运行 Android 6.0 系统的 Zenfone2 手机上可以正常使用第一段的两条命令控制系统 NFC 功能。</p>
<h3 id="gps-开关-开启关闭百度地图">GPS 开关 + 开启关闭百度地图</h3>
<p>需求还是和之前的文章相同：</p>
<ol>
<li>刷 NFC 标签自动开启 GPS 和百度地图进行导航：</li>
<li>导航完成后再刷一次 NFC 标签自动关闭百度地图并关闭 GPS。</li>
</ol>
<p>为此我还是只建立一个 Tasker 任务，包含多个操作：</p>
<ol>
<li>Tasker 操作类别中选 <strong>变量</strong> -&gt; <strong>变量设置</strong>，<code>%TMPGPS</code> 发往 <code>%GPS</code> <br />
先保存 <code>%GPS</code> 这个 Tasker 自带的 GPS 设置变量；</li>
<li>如果 <code>%GPS = off</code> 则启动应用 -&gt; 百度地图；</li>
<li>如果 <code>%GPS = off</code> 则运行外壳开启 GPS；</li>
<li>如果 <code>%TMPGPS != on</code> 则停止运行任务（操作类别中选 <strong>任务</strong> -&gt; <strong>停止</strong>）忽略后续操作；</li>
<li>按钮 -&gt; 后退（操作类别中选 <strong>输入</strong> -&gt; <strong>按钮</strong>），此操作需要添加多个以模拟持续按返回键退出百度地图；</li>
<li>杀死应用 -&gt; 百度地图；</li>
<li>运行外壳关闭 GPS。</li>
</ol>
<p>通过 Shell 命令开启关闭 GPS 也稍微有点特殊，需要使用下面两条命令：</p>
<pre class="brush: bash; title: ; notranslate">
settings put secure location_providers_allowed +gps
settings put secure location_providers_allowed -gps
</pre>
<h2 id="后记">后记</h2>
<p>为了方便查询，我把一些可能经常用到的 Android Shell 控制命令也整理保存到我的 Wiki 知识库中了，读者可以点击 Wiki 链接 <a href="https://wiki.zohead.com/%E6%8A%80%E6%9C%AF/Android/Android%20Shell%E6%8E%A7%E5%88%B6%E6%89%8B%E6%9C%BA.md" target="_blank">Android Shell 控制手机</a> 查看。后续有其它 Shell 命令需要加进来或者修改时，我也会尽量及时更新该文档的，最后祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/tasker-shell/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>使用DisplayLink显卡组装Android Superbook</title>
		<link>https://zohead.com/archives/displaylink_superbook/</link>
		<comments>https://zohead.com/archives/displaylink_superbook/#comments</comments>
		<pubDate>Tue, 26 Jul 2016 16:31:39 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[Andromium]]></category>
		<category><![CDATA[DisplayLink]]></category>
		<category><![CDATA[Kickstarter]]></category>
		<category><![CDATA[LapDock]]></category>
		<category><![CDATA[Motorola]]></category>
		<category><![CDATA[OTG]]></category>
		<category><![CDATA[Superbook]]></category>
		<category><![CDATA[USB]]></category>
		<category><![CDATA[Zenfone 2]]></category>
		<category><![CDATA[显卡]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1257</guid>
		<description><![CDATA[关于 Superbook 前几天在 Kickstarter 上看到一个挺有意思的 Superbook 众筹项目，其主要目的是以 99 美元的价格实现 Android 手机插上数据线连接 Superbook 就能变身笔记本，该项目由 Andromium 团队推出，截至我写此文章的时候该项目已经筹集到 80 多万美元，感兴趣的朋友可以看看其 Kickstarter 项目主页： http://getsuperbook.com/ 初看该项目介绍，发现还是比较类似 Motorola 前几年推出的 LapDock 笔记本扩展坞装备的，只是 LapDock 是专门给 Motorola ME865 等手机设计 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="关于-superbook">关于 Superbook</h2>
<p>前几天在 Kickstarter 上看到一个挺有意思的 Superbook 众筹项目，其主要目的是以 99 美元的价格实现 Android 手机插上数据线连接 Superbook 就能变身笔记本，该项目由 Andromium 团队推出，截至我写此文章的时候该项目已经筹集到 80 多万美元，感兴趣的朋友可以看看其 Kickstarter 项目主页：</p>
<p><a href="http://getsuperbook.com/">http://getsuperbook.com/</a></p>
<p>初看该项目介绍，发现还是比较类似 Motorola 前几年推出的 LapDock 笔记本扩展坞装备的，只是 LapDock 是专门给 Motorola ME865 等手机设计的，其它设备如果想要使用 LapDock 必须支持 USB Host/OTG 以及 HDMI 输出功能而且需要准备各种转换线。</p>
<p>Superbook 主要特性包括：</p>
<ul>
<li>可支持所有包含 USB OTG 功能的 Android 手机；</li>
<li>11.6 寸 1366x768 的 IPS 屏幕；</li>
<li>10 个小时的续航支持，连接上手机后可以为手机充电；</li>
<li>也可以支持树莓派、MiniPC 等类型的设备；</li>
<li>在自带了键盘和多点触控触摸板的基础上也有扩展的 USB 接口方便用户使用；</li>
<li>与手机共享数据，这点和 LapDock、华硕 PadFone 底座类似。</li>
</ul>
<p>Android 手机连接 Superbook 之后，Superbook 上运行的是 <strong>Andromium OS</strong> 界面，<strong>Andromium OS</strong> 也是支持多窗口运行 Android App 的，其界面和 <a href="http://www.jide.com/remixos-for-pc">Remix OS</a> 还是有点相似的。</p>
<p>在稍微看了 Superbook 的技术细节之后，我发现其不需要手机支持 HDMI 或者 MHL 等视频输出功能，而是基于 DisplayLink 提供的 Android 显示输出功能实现的，具体可以参考 <a href="http://www.displaylink.com/downloads/android">DisplayLink for Android</a> 的介绍页面。</p>
<p>这样看起来 Superbook 和 LapDock 的最大区别就是 Superbook 在笔记本扩展坞自身的 USB Hub 上额外集成了一块 DisplayLink 的 USB 显卡以支持 Android 手机的 USB 显卡视频输出功能。这样不仅可以兼容更多种类的 Android 手机，而且只需要一根 USB 数据线也能让用户使用起来更简单。</p>
<p>刚好我手头上还有一块之前捣鼓 Ubuntu for Android 时购买的 DisplayLink USB 2.0 接口的显卡，应该就可以配合闲置的 Motorola LapDock 以及支持 USB OTG 功能的华硕 Zenfone2 手机（刚好不支持 HDMI 或 MHL 视频输出功能）来自己组装一个简陋版本的 Superbook 咯。</p>
<h2 id="手机-lapdock-组装-superbook">手机 + LapDock 组装 Superbook</h2>
<p>如果读者也有兴趣自己组装一台 Superbook 试试看，首先需要确认你的手机支持 USB OTG 功能，还需要一块 DisplayLink 的 USB 显卡，如果能外接 USB 或者蓝牙的键盘或者鼠标的话操作也会更方便，另外如果显示设备不是 LapDock 的话那最好还需要一个带电源的 USB Hub 给显卡供电（LapDock 直接带键盘鼠标并支持供电就不用考虑这些了）。</p>
<p>首先手机上需要安装 <a href="https://play.google.com/store/apps/details?id=com.displaylink.presenter">DisplayLink Presenter</a> App，可以通过 Google Play 商店直接安装，安装之后无需做任何设置，只要手机接上了 DisplayLink 显卡或者扩展坞该 App 就会自动开始工作，该 App 界面中的说明：</p>
<div style="width: 370px" class="wp-caption alignnone"><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737370806/displaylink_presenter.jpg" width="360" height="640" alt="DisplayLink Presenter" /><p class="wp-caption-text">DisplayLink Presenter</p></div>
<p>接着就可以把手机和 USB 显卡以及 LapDock 连接起来了，还请忽略图中各种凌乱的转接头和连接线 ^_^：</p>
<div style="width: 650px" class="wp-caption alignnone"><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442947/superbook_lapdock.jpg" width="640" height="360" alt="LapDock 连接" /><p class="wp-caption-text">LapDock 连接</p></div>
<p>Zenfone2 手机直接连 LapDock 的 Micro-USB 接口，DisplayLink USB 显卡插在 LapDock 的扩展 USB 接口上（见上图圈出的地方），并通过 HDMI 线与 LapDock 的 Micro-HDMI 接口相连，这样打开 LapDock 盖子的时候，LapDock 就能同时给手机和 USB 显卡供电。</p>
<p>此时我们可以先确认下 USB 显卡设备是否已经找到，在终端中运行 <code>busybox lsusb</code> 命令（Android 需要 busybox 支持）可以看到手机中所有的 USB 设备，下面的截图是插上 USB 显卡之前和之后的输出信息对比：</p>
<div style="width: 650px" class="wp-caption alignnone"><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442947/superbook_usb_dev.jpg" width="640" height="314" alt="DisplayLink USB 显卡设备" /><p class="wp-caption-text">DisplayLink USB 显卡设备</p></div>
<p>上面命令输出中设备 ID 为 <code>17e9:03c1</code> 的 USB 设备就是 DisplayLink USB 显卡了。</p>
<p>一切正常的话，这时应该就能在 LapDock 屏幕上看到手机屏幕的镜像了，通过 LapDock 的键盘和鼠标还能更方便的进行输入操作，应该可以初步达到 Superbook 的效果咯。这个是我在 LapDock 上运行 Android Chrome 浏览器的效果，屏幕大了看起来更方便：</p>
<div style="width: 650px" class="wp-caption alignnone"><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442950/superbook_web.jpg" width="640" height="360" alt="LapDock Superbook 浏览网页" /><p class="wp-caption-text">LapDock Superbook 浏览网页</p></div>
<p>还可以用来玩手机游戏，不过游戏最好是支持横屏的，能支持鼠标（触摸板）和键盘控制的话效果更好。当然如果你玩的游戏能支持 USB 或者蓝牙手柄，那玩起来就更加 happy 一些咯：</p>
<div style="width: 650px" class="wp-caption alignnone"><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442946/superbook_game.jpg" width="640" height="378" alt="LapDock Superbook 玩游戏" /><p class="wp-caption-text">LapDock Superbook 玩游戏</p></div>
<p>当然到这一步还只是通过 DisplayLink 显卡实现了简单的手机屏幕镜像的功能，如果你需要实现和未来 Superbook 类似的体验，可以安装 Google Play 商店中的 <strong>Andromium OS</strong> App 了，链接如下：</p>
<p><a href="https://play.google.com/store/apps/details?id=com.andromium.os">https://play.google.com/store/apps/details?id=com.andromium.os</a></p>
<p><strong>Andromium OS</strong> App 说明中虽然说的是目前支持手机连接兼容的 Dock 扩展坞，不过像我这样直接使用 DisplayLink USB 显卡也是可以的哦。</p>
<p>具体连接和操作步骤可以参考 Andromium 论坛中的<a href="http://andromiumos.freeforums.net/thread/5/setting-andromium-samsung-phone-dock">介绍帖子</a>，其实和上面的连接方法是基本一样的，只是将 DisplayLink Presenter App 换成了 Andromium OS，而且使用方式上还是有一些需要注意的限制。</p>
<h2 id="后记">后记</h2>
<p>虽然本文只是写了下最简单的使用 LapDock 搭配 DisplayLink 显卡组装一个简陋版 Superbook 的方法，但从 Superbook 的 Kickstarter 项目在 38 个小时内筹集到 50 万美元的速度来看，Superbook 还算是比较引人关注的。</p>
<p>官方也说明了筹款额达到 100 万美元之后会考虑增加 1080p 屏幕的版本（额外增加 30 美元），如果 129 美元的 1080p 版本的 Superbook 能够正常生产出来而且比较稳定的话，我应该会考虑入手的，至少使用上还是比 LapDock 这种要方便很多的。另外毕竟现在 Android 手机的硬件配置已经比较强悍了，这样我一直比较期望的 Linux on Android 效果在稍做修改之后应该也能在 Superbook 上发挥出来了。</p>
<p>总之还是希望 Andromium 的 Superbook 项目能够顺利完成的哦，最后祝大家在这个火热的季节还能玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/displaylink_superbook/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>为Zenfone 2开启NFC SIM卡刷公交支持</title>
		<link>https://zohead.com/archives/zenfone2-nfc-sim/</link>
		<comments>https://zohead.com/archives/zenfone2-nfc-sim/#comments</comments>
		<pubDate>Wed, 06 Jul 2016 17:34:35 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[生活]]></category>
		<category><![CDATA[NFC]]></category>
		<category><![CDATA[SE]]></category>
		<category><![CDATA[SIM]]></category>
		<category><![CDATA[SWP]]></category>
		<category><![CDATA[Zenfone 2]]></category>
		<category><![CDATA[公交卡]]></category>
		<category><![CDATA[华硕]]></category>
		<category><![CDATA[支付]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1246</guid>
		<description><![CDATA[我在 4 月份买的华硕 Zenfone 2 ZE551ML 手机已经上手使用一段时间了，当初也看中这款 Zenfone 2 ZE551ML 手机是支持 NFC 功能的，测试使用 NFC 标签配合 Trigger+Tasker 触发任务或者 Smart Lock 智能解锁都是比较好用的。同样我还试过开启招商银行的「掌上生活」App 中的「一闪通」功能（其实就是银联云闪付）进行 NFC 刷手机支付。 不过在使用中国联通的 NFC SIM 卡进行刷公交或者地铁时发现存在问题，根本无法使用，鉴于网上也有不少别的用户碰到这种问题（比较坑的是有人已经给 NFC SIM 卡充完值才发现刷公交或地铁没反应）， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>我在 4 月份买的<a href="https://zohead.com/archives/zenfone2/">华硕 Zenfone 2 ZE551ML 手机</a>已经上手使用一段时间了，当初也看中这款 Zenfone 2 ZE551ML 手机是支持 NFC 功能的，测试使用 NFC 标签配合 <a href="https://zohead.com/archives/tasker-nfc/">Trigger+Tasker 触发任务</a>或者 Smart Lock 智能解锁都是比较好用的。同样我还试过开启招商银行的「掌上生活」App 中的「一闪通」功能（其实就是银联云闪付）进行 NFC 刷手机支付。</p>
<p>不过在使用中国联通的 NFC SIM 卡进行刷公交或者地铁时发现存在问题，根本无法使用，鉴于网上也有不少别的用户碰到这种问题（比较坑的是有人已经给 NFC SIM 卡充完值才发现刷公交或地铁没反应），这里我把解决的方法写出来供大家参考。</p>
<h2 id="nfc-问题说明">NFC 问题说明</h2>
<p>首先说下开启联通 NFC SIM 卡刷公交功能的流程：</p>
<ol>
<li>首先确认所在城市以及你使用的移动运营商支持 NFC SIM 卡刷公交，这个到网上都可以找到相应的介绍，我所用的南京联通是和智汇市民卡合作支持手机 SIM 卡刷公交的；</li>
<li>在中国联通沃支付网站上确认你所使用的手机是否在其「手机钱包」业务的兼容机型列表中： <br />
<a href="https://epay.10010.com/wop/wallet">https://epay.10010.com/wop/wallet</a></li>
<li>去营业厅将手机 SIM 卡更换为 NFC SWP SIM 卡，联通的话必须去其自有营业厅办理，社会营业厅无法办理，换 NFC SWP SIM 卡一般是免费的；</li>
<li>接着需要在自有营业厅开通手机钱包业务；</li>
<li>下载中国联通<a href="https://epay.10010.com/">沃钱包</a> App，可以在「沃钱包」App 中注册联通沃支付账户；</li>
<li>在「沃钱包」App 中安装「手机钱包」卡包功能插件；</li>
<li>确认「卡包」中能找到手机公交卡就可以在公交地铁充值网点进行充值及手机刷公交和地铁了。</li>
</ol>
<p>刚好我使用的 Zenfone 2 手机是在联通手机钱包业务兼容机型列表中，Android 系统版本是 <code>5.0.2</code>，打开「沃钱包」App（版本：<code>3.5.0</code>）也能正常安装「手机钱包」卡包功能插件（版本：<code>1.4.0</code>）：</p>
<div style="width: 370px" class="wp-caption alignnone"><img alt="沃钱包 App" src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442970/wo-wallet.jpg" width="360" height="640" /><p class="wp-caption-text">沃钱包 App</p></div>
<p>进入「卡包」功能还能认到手机 NFC SIM 卡的公交卡：</p>
<div style="width: 370px" class="wp-caption alignnone"><img alt="NFC SIM 公交卡" src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442861/nfc-sim-bus-card.jpg" width="360" height="640" /><p class="wp-caption-text">NFC SIM 公交卡</p></div>
<p>不过悲剧的就是拿着手机到任何一个公交或者地铁充值点都不能识别到卡信息，充值机或者公交刷卡机、地铁闸机都没有任何反应，当然也没有办法充值或者刷卡了。</p>
<h2 id="问题分析">问题分析</h2>
<p>Android 手机上的 NFC 支付功能是使用 NFC 的卡模拟工作方式，出于对 NFC 通信安全的考虑，如果需要使用 NFC 卡模拟，除了手机自带的 NFC 控制器外，还需要 SE（Secure Element）元件即安全元件，它的作用是用来存储用户的敏感信息，例如银行卡卡号、密码之类的，其一般采用硬件加密方式。</p>
<p>目前 Android 上常见的 NFC 卡模拟 SE 方案有：</p>
<ul>
<li>SE 元件内置在手机中；</li>
<li>SE 元件内置在 SIM 卡中；</li>
<li>SE 元件内置在专用 SD 卡中；</li>
<li>Google 推出的 HCE 技术，即 Host-based card emulation。</li>
</ul>
<p>前 3 种由于需要特定手机或者 SIM 卡、SD 卡等硬件支持，因此 Google 搞了基于主机的卡模拟模式，如果使用这种方案系统可以将一个 App 或者一个云端作为一个虚拟 SE 来实现 NFC 支付，这样可以摆脱对硬件的依赖。Android 5.0 或者 6.0 之后 Google 也默认使用 HCE 方式的主机卡模拟方案。</p>
<p>像本文开头说的招商银行一闪通就是使用的第一种手机内置 SE 方案，而今天我们所要使用的 NFC SIM 卡当然就是用的第二种 SIM SWP 方案，也是广大移动运营商们主推的方案，经过搜索我发现华硕 Zenfone 2 采用的 Broadcom BCM20795 NFC 芯片是支持 SIM SWP 方案的，联通手机钱包业务也说明 Zenfone 2 已经通过 NFC 支付测试。</p>
<p>另外我还发现一个有趣的现象就是虽然 Zenfone 2 手机开机点亮屏幕的状态下刷公交或者地铁没有任何反应，但如果我重启手机并在刚刚显示启动 Logo 的时候刷公交却是有反应的（只是没有充值提示余额不足），这就说明手机刚开机的时候 NFC SIM SWP 是能正常工作的，启动完成进入系统之后反而不行了。</p>
<p>后来我还看到有网友在华硕官方论坛上反映了这个问题，并建议华硕在手机设置中增加类似于一加手机中的 NFC 支付使用手机内置 SE 还是 SIM 卡 SE 的选项，不过华硕并没有对此进行更新处理，因此我认为 Zenfone 2 硬件是支持 NFC SIM SWP 支付的，只是由于系统 ROM 原因不能正常使用，应该可以通过其它方法开启。</p>
<h2 id="修改配置支持刷-sim-卡">修改配置支持刷 SIM 卡</h2>
<p>经过一番搜索之后我发现网上有 Nexus 6 用户也碰到类似的问题：</p>
<p>*<a href="https://paoyuan.org/2015/11/14/nexus-6-android-marshmallow-nfc-swp-sim-support" target="_blank">NEXUS 6 ANDROID MARSHMALLOW NFC SWP-SIM 支援</a></p>
<p>可以通过修改 Android 系统的 Broadcom NFC 配置文件来开启手机的 SIM SWP 支持，我的 Zenfone 2 手机 NFC 配置和上面的 Nexus 6 配置有一些差别，经过多次修改测试发现现在也可以使 NFC SIM SWP 功能正常工作了。</p>
<p>NFC 配置文件的修改都需要先开启手机的 root 权限哦，首先修改 <code>/system/etc/libnfc-brcm.conf</code> 配置文件增加以下内容：</p>
<pre class="brush: diff; title: /system/etc/libnfc-brcm.conf; notranslate">
+NFA_MAX_EE_SUPPORTED=3
+ACTIVE_SE=0xF3
+DEFAULT_ISODEP_ROUTE=0xF3
+NFA_HCI_STATIC_PIPE_ID_F3=0x70
+NFA_HCI_STATIC_PIPE_ID_F4=0x71
+UICC_LISTEN_TECH_MASK=0x0
</pre>
<blockquote>
<p><strong>注意</strong></p>
<p>注意上面显示的是 patch 文件格式，<code>-</code> 号开头的为需要删除的内容，<code>+</code> 号开头的为需要增加的内容，实际修改时请删除开头的 <code>-</code> 号或 <code>+</code> 号。</p>
</blockquote>
<p>上面的 <code>NFA_MAX_EE_SUPPORTED</code> 值为 3 表示开启多种 NFC SE 元件支持，<code>ACTIVE_SE</code> 和 <code>DEFAULT_ISODEP_ROUTE</code> 都设为 0xF3 表示默认使用 SIM SWP SE 元件，剩下的 3 个参数不一定需要设置。</p>
<p>接着修改 <code>/system/etc/libnfc-brcm-20795a20.conf</code> 配置文件：</p>
<pre class="brush: diff; title: /system/etc/libnfc-brcm-20795a20.conf; notranslate">
 ###############################################################################
 # Pre-Discovery Startup Configuration (256 bytes maximum)
-NFA_DM_PRE_DISCOVERY_CFG={0A:C2:08:02:00:C3:04:40:4B:4C:00}
+NFA_DM_PRE_DISCOVERY_CFG={0A:C2:08:01:00:C3:04:40:4B:4C:00}
</pre>
<p>上面的这两个配置文件修改完成之后重启手机，到公交或者地铁充值点去充值刷卡应该就有反应了哦，不过需要注意默认还是只有手机点亮的情况刷卡才有反应。</p>
<h2 id="后记">后记</h2>
<p>NFC SIM 卡的公交卡余额充值功能在各地似乎不太一样，上海之类城市是支持直接在联通的「沃钱包」App 中进行充值的，而我这边「沃钱包」的卡包列表中虽然能看到南京 NFC SIM 卡的智汇卡，但并不能正确显示卡中的余额，也没法直接通过手机 App 进行充值。</p>
<p>总体来说联通的「沃钱包」App 的使用体验还是挺符合其企业形象的，一贯的很难用而且不稳定，像下面这种 App 中找不到 NFC SIM 卡包的报错还是经常会碰到：</p>
<div style="width: 370px" class="wp-caption alignnone"><img alt="沃钱包找不到 NFC SIM 卡包" src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442972/wo-wallet-error.jpg" width="360" height="640" /><p class="wp-caption-text">沃钱包找不到 NFC SIM 卡包</p></div>
<p>当然最后还是要说一下本文只是在南京公交地铁以及联通 NFC SIM 卡环境下使用测试的，如果你使用的环境不同并不能保证就一定能把 NFC SIM 支付用起来哦，祝玩的开心～～～。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/zenfone2-nfc-sim/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>为MK809IV电视棒开启真正1080p支持</title>
		<link>https://zohead.com/archives/mk809iv-1080p/</link>
		<comments>https://zohead.com/archives/mk809iv-1080p/#comments</comments>
		<pubDate>Sun, 01 May 2016 19:42:48 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[生活]]></category>
		<category><![CDATA[1080p]]></category>
		<category><![CDATA[MK809IV]]></category>
		<category><![CDATA[RK3188]]></category>
		<category><![CDATA[拆机]]></category>
		<category><![CDATA[电视棒]]></category>
		<category><![CDATA[迷你PC]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1221</guid>
		<description><![CDATA[去年为了能把宿舍客厅的电视利用起来我购入了一款国产 MK809IV 安卓电视棒，事实证明这个决定还是比较明智的，装了多个 TV 版的 App 之后用起来还是比较顺畅的。特别是光腚总局各种针对互联网电视盒子的禁令出台之后，我用的这款 MK809IV 迷你 PC 完全不受影响，不用担心像某猫之类的盒子会受到自动删除 App 之类的待遇。 我买的这款 MK809IV 电视棒采用的处理器是 Rockchip RK3188-T，自带 2GB 运行内存，ROM 容量则是 8GB 的，运行 Android 4.4 版本系统，无线网络支持 802.11 b/g/n（这点比较可惜，用不上我的路由器的 5GHz  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>去年为了能把宿舍客厅的电视利用起来我购入了一款国产 MK809IV 安卓电视棒，事实证明这个决定还是比较明智的，装了多个 TV 版的 App 之后用起来还是比较顺畅的。特别是光腚总局各种针对互联网电视盒子的禁令出台之后，我用的这款 MK809IV 迷你 PC 完全不受影响，不用担心像某猫之类的盒子会受到自动删除 App 之类的待遇。</p>
<p>我买的这款 MK809IV 电视棒采用的处理器是 Rockchip RK3188-T，自带 2GB 运行内存，ROM 容量则是 8GB 的，运行 Android 4.4 版本系统，无线网络支持 802.11 b/g/n（这点比较可惜，用不上我的路由器的 5GHz WiFi），电视棒外观是这样的：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442846/mk809iv.jpg" alt="MK809IV外观" title="MK809IV外观"></p>
<p>左边是标准 HDMI 接口用于接电视或显示器，下方依次是用于刷机的 Reset 插孔、MicroSD 卡插槽、Micro USB 电源/OTG 接口，右边则是一个标准 USB 和一个 Micro USB Host 接口，正面红色的就是可移动外置天线。</p>
<p>由于做类似电视棒的国内寨厂有好多个，即使是同一型号的 MK809 在淘宝上也能搜索到多个不同的产品，而且我买的 MK809IV 包装盒上也没有看到任何厂家信息，所以使用的时候如果发现有问题售后其实还是稍微有点麻烦的。</p>
<p>最近我发现这款电视棒的系统存在一个问题就是虽然已经在显示设置中将视频输出的分辨率改为 1920x1080 了，而且电视上也确实提示当前 HDMI 视频输入是 1080p 的，但在 Android 系统里运行安兔兔等检测软件看到的分辨率却是 1280x720 的，另外无论视频播放还是用电视棒自带的 XBMC App 看到的分辨率也都是 720p 的。后来搜索发现国外有用户也发现了这个问题，看看 YouTube 上的这个视频：</p>
<p><iframe width="560" height="315" src="https://www.youtube.com/embed/Emxw080IyBk" allowfullscreen=""></iframe></p>
<p>看来厂家不知道是为了系统流畅性还是散热方面的考虑，将能支持 1080p 视频输出的 RK3188-T 系统人为限制为了 720p。后来看到国外有网友说通过刷新的固件可以开启 1080p 支持，因此接着就是找到合适的固件来升级了。</p>
<p>刚开始我找到的是 Ugoos 的 <a href="http://ugoos.net/ugoos-mk809iv-rk3188">UG-MK809IV</a> 这款型号和配置看起来和我的 MK809IV 一样的设备，Ugoos 非常厚道的为他们的设备提供了支持 1080p 的固件，下载完固件之后准备在 Windows 系统上刷入。</p>
<p>这里简单说下这款电视棒的刷机流程，首先可以在 Android 下用一键关机重启之类的 App 重启到刷机模式，也可以拿针或者镊子之类的工具按下 Reset 按钮并通过 Micro USB OTG 接口接在电脑上，刷机模式下需要先安装 Rockchip 驱动，驱动我已经分享到百度云盘：</p>
<p><a href="http://pan.baidu.com/s/1o8QXrKm">http://pan.baidu.com/s/1o8QXrKm</a></p>
<p>下载安装完成之后先确认设备管理器能正常找到设备：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442848/mk809iv-device.jpg" alt="MK809IV设备驱动" title="MK809IV设备驱动"></p>
<p>接下来需要用到 Rockchip Batch Tool 刷机工具，同样可以通过百度云盘下载：</p>
<p><a href="http://pan.baidu.com/s/1b8OCFk">http://pan.baidu.com/s/1b8OCFk</a></p>
<p>打开刷机工具，选择下载并解压缩出来的固件文件之后如果一切正常的话刷机工具的 Connected Devices 里应该能识别到设备：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442851/mk809iv-flash-tool.jpg" alt="Rockchip刷机工具" title="Rockchip刷机工具"></p>
<blockquote>
<p><strong>注意</strong></p>
<p>如果你用于刷机的 Windows 机器只有 USB 3.0 接口，没有 USB 2.0 接口，那有可能出现刷机工具认不到设备的问题，这个时候需要修改 Rockchip Batch Tool 中的 <code>config.ini</code> 配置文件，将 <code>SUPPORTLOWUSB</code> 改为 <code>TRUE</code> 才能让刷机工具识别到 USB 3.0 接口上的电视棒设备。</p>
</blockquote>
<p>点击 <code>Restore</code> 按钮（注意不是 <code>Upgrade</code> 按钮）就可以开始刷机，刷机完成之后提示 Restore Done Success，接着设备会自动重启：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442851/mk809iv-flash-ok.png" alt="MK809IV刷机" title="MK809IV刷机"></p>
<p>看起来一切顺利，只是比较悲催的是刷机成功之后重新插上电视发现无法正常启动了，电视没有检测到任何信号输入。看来是 Ugoos 的 UG-MK809IV 硬件和我的 MK809IV 电视棒并不匹配因此下载的固件也不兼容，这样只能重新再尝试别的固件了。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果刷机成功之后电视棒无法正常启动，重新启动到刷机模式连上电脑也无法正常识别到设备（例如出现 <em>请求 USB 设备描述符失败</em> 等错误），那可能需要将电视棒连接到 USB 2.0 接口上重新输入正确的固件。</p>
</blockquote>
<p>为了能找到适合我这款电视棒的固件，准备寄出拆解大招看看具体的硬件配置，简单拆开后的正面图：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442854/mk809iv-teardown-front.jpg" alt="MK809IV拆解正面" title="MK809IV拆解正面"></p>
<p>具体内部做工不好评价，先看看正面有哪些芯片：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442852/mk809iv-ram-rom-chip.jpg" alt="MK809IV RAM和ROM芯片" title="MK809IV RAM和ROM芯片"></p>
<p>可以看到主板左边的是 SK Hynix 的 H27UCG8T2ETR-BC 容量为 8GB 的 ROM 芯片，右边的 RAM 则是由 4 颗（正面两颗、背面两颗）镁光（Micron）的 D9QBJ 也就是 MT41K512M8RH-125 SDRAM 芯片组成，下方则是 IT66121FN HDMI 芯片，这是联阳科技（ITE）推出的一款低功率单通道 HDMI 传输器芯片，看看主板正面的左上角：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442855/mk809iv-wifi-chip.jpg" alt="MK809IV无线芯片" title="MK809IV无线芯片"></p>
<p>这个是关注的重点之一了，这款电视棒使用的是 Realtek 公司的 RTL8723BS 这颗支持蓝牙、WiFi、FM 三合一模块的无线芯片，这时才发现这款芯片工作在 802.11 b/g/n 模式下最高才支持 150Mbps 的无线传输速率，虽然也支持蓝牙 4.0 了，但这种无线速率真是白瞎了我买的<a href="https://zohead.com/archives/newifi-mini-openwrt/">千兆无线路由器</a>了。</p>
<p>至于主板背面，撕掉散热贴之后就能看到另外两颗 SDRAM 芯片和主处理器了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442847/mk809iv-cpu.jpg" alt="MK809IV处理器" title="MK809IV处理器"></p>
<p>主处理器是 Rockchip 的 RK3188-T，这是主流使用的 RK3188 的降频版，最高只支持 1.4GHz，整套芯片的硬件成本应该也会更低，比较坑的是几乎所有商家都会拿 RK3188 来宣传而不会主动说明他们的电视棒产品使用的很有可能是降频的处理器，最后看看主板背面的左上方：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442851/mk809iv-pm-chip.jpg" alt="MK809IV电源管理芯片" title="MK809IV电源管理芯片"></p>
<p>可以看到这里用的是几乎 Rockchip 标配的 ACT 公司的 ACT8846QM 电源管理芯片，下方就是 MicroSD 插槽。</p>
<p>这下了解了 MK809IV 电视棒的所有硬件配置，可以按照相同的硬件情况搜索符合的固件了，终于还是在 Ugoos 网站上看到 <a href="http://ugoos.net/ugoos-oem/mk809iii-new-style">UG-MK809III new style</a> 这款产品的配置与我的 MK809IV 相符，其固件下载页面也提供了几种不同的固件，分别对应使用 MTK、AP6210、AP6330、Realtek RTL8723BS 这几种无线芯片的电视棒设备，比较幸运的是我这款 MK809IV 设备使用的是 RTL8723BS 芯片，而且刚好有支持 1080p 的固件。</p>
<p>从 Ugoos 提供的 Mega 网盘地址下载固件：</p>
<p><a href="https://mega.nz/#!Z10mgYhT!SdubLh6SJZ1OYsyrCR80G8kaowNLAhNMbX1bNeIrPBs">https://mega.nz/#!Z10mgYhT!SdubLh6SJZ1OYsyrCR80G8kaowNLAhNMbX1bNeIrPBs</a></p>
<p>重新使用 Rockchip Batch Tool 刷机工具更新系统之后重启，终于能看到令人欣喜的新系统启动画面了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442847/mk809iv-boot.jpg" alt="MK809IV启动画面" title="MK809IV启动画面"></p>
<p>新系统还是 Android 4.4 版本的（发布日期：2014-09-24），下面可以运行 XBMC 之类的 App 确认效果了，从下图同样也可以看到视频输出的分辨率终于是真正的 1080p 了：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442855/mk809iv-xbmc.jpg" alt="MK809IV 1080p视频输出" title="MK809IV 1080p视频输出"></p>
<p>最后经过这些天使用新固件的体验来看，安装多个 TV 端视频 App 的播放效果依然是比较流畅的，除了在播放 1080p 等高清视频时设备本身发热稍微有点多之外看起来没有其它影响咯。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/mk809iv-1080p/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>修改Remix OS适配Chromebook键盘</title>
		<link>https://zohead.com/archives/remixos-cb-keyboard/</link>
		<comments>https://zohead.com/archives/remixos-cb-keyboard/#comments</comments>
		<pubDate>Tue, 23 Feb 2016 18:01:03 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Chromebook]]></category>
		<category><![CDATA[keylayout]]></category>
		<category><![CDATA[Remix OS]]></category>
		<category><![CDATA[扫描码]]></category>
		<category><![CDATA[键盘]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1178</guid>
		<description><![CDATA[按照之前博客里方法在我的 Dell Chromebook 11 上捣鼓使用 ext4 格式 Remix OS U 盘一段时间之后发现对于 Chromebook 上的 Remix OS PC 版一个普遍存在的问题就是 Chromebook 特殊键盘按键功能在 Remix OS 上都不能正常工作了，这对于习惯了 Chrome OS 便利性的我来说还是略不爽的，因此想办法修改了 Remix OS 系统文件以适配 Chromebook 键盘，这里给 Chromebook 用户一个参考。 Chromebook 上的特殊按键包括最上面一排的功能按键和专用搜索键； 功能按键和普通 PC 机上的 F1 - F [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>按照之前博客里方法在我的 Dell Chromebook 11 上捣鼓使用 ext4 格式 Remix OS U 盘一段时间之后发现对于 Chromebook 上的 Remix OS PC 版一个普遍存在的问题就是 Chromebook 特殊键盘按键功能在 Remix OS 上都不能正常工作了，这对于习惯了 Chrome OS 便利性的我来说还是略不爽的，因此想办法修改了 Remix OS 系统文件以适配 Chromebook 键盘，这里给 Chromebook 用户一个参考。</p>
<blockquote>
<p>Chromebook 上的特殊按键包括最上面一排的功能按键和专用搜索键； <br />
  功能按键和普通 PC 机上的 F1 - F12 功能按键有点类似，包括专门为 Chrome OS 设计的前进、后退、全屏、显示亮度控制、音量控制 等按键； <br />
  有关 Chromebook 键盘的相关信息请参考 Google 官方帮助文档 <a href="https://support.google.com/chromebook/answer/1047364">Use your Chromebook keyboard</a> 。</p>
</blockquote>
<p>首先可以用 Crouton Linux 环境中的命令行 <code>showkey</code> 命令或者图形界面下的 <code>xev</code> 命令检查 Chromebook 上特殊按键的 scan code，确认之后发现：</p>
<blockquote>
<p>直接运行 Remix OS 的话也可以安装 <a href="https://play.google.com/store/apps/details?id=aws.apps.keyeventdisplay">KeyEvent Display</a> 这款 App 来确认 Chromebook 上按键的 scan code。</p>
</blockquote>
<ul>
<li>Chromebook 搜索键的 scan code 为：<strong>125</strong>；</li>
<li>Chromebook 上面一排从后退键到提高音量键这些特殊功能键的 scan code 为：<strong>59 - 68</strong>；</li>
<li>特殊功能键的 scan code 和普通 PC 上的 F1 - F10 一致。</li>
</ul>
<blockquote>
<p><strong>注意</strong></p>
<p>不同 Chromebook 上特殊按键的 scan code 也有可能和上面的结果不同，我列出的结果适用于 Dell Chromebook 11 系列。</p>
</blockquote>
<p>知道这些特殊按键的 scan code 之后就可以修改 Android keylayout 以适配 Chromebook 键盘，不过这一步需要修改 Remix OS PC 版 system 目录中的文件。如果你是按照我写的 <a href="https://zohead.com/archives/cb-ext4-remixos/">Chromebook使用ext4 U盘运行Remix OS</a> 文章中的方法安装的，那只要安装过程中启用了 system 目录的可写功能，那可以直接在 Remix OS（Remix OS 中直接修改需要开启 root 权限） 或者 Chrome OS 里修改还是很方便的。</p>
<blockquote>
<p>如果你是用技德官方的工具烧写的 U 盘那会稍微麻烦点，需要在 Chromebook 等 Linux 系统中挂载 system 映像文件修改并重新打包，网上应该也能找到对应的教程，这里就不做详细介绍了。</p>
</blockquote>
<p>修改 Remix OS 中的 <code>/system/usr/keylayout/Generic.kl</code> 文件，首先注释掉这些已有的行（Android 默认的键盘 scan code 到 key code 映射）：</p>
<pre class="prettyprint"><code class="language-bash hljs "><span class="hljs-comment">#key 59    F1</span>
<span class="hljs-comment">#key 60    F2</span>
<span class="hljs-comment">#key 61    F3</span>
<span class="hljs-comment">#key 62    F4</span>
<span class="hljs-comment">#key 63    F5</span>
<span class="hljs-comment">#key 64    F6</span>
<span class="hljs-comment">#key 65    F7</span>
<span class="hljs-comment">#key 66    F8</span>
<span class="hljs-comment">#key 67    F9</span>
<span class="hljs-comment">#key 68    F10</span>
<span class="hljs-comment">#key 125   META_LEFT</span></code></pre>
<p>然后增加这些行：</p>
<pre class="prettyprint"><code class="language-bash hljs ">key <span class="hljs-number">59</span>    MEDIA_PREVIOUS
key <span class="hljs-number">60</span>    MEDIA_NEXT
key <span class="hljs-number">61</span>    MEDIA_PLAY_PAUSE
key <span class="hljs-number">62</span>    MENU
key <span class="hljs-number">63</span>    APP_SWITCH
key <span class="hljs-number">64</span>    BRIGHTNESS_DOWN
key <span class="hljs-number">65</span>    BRIGHTNESS_UP
key <span class="hljs-number">66</span>    VOLUME_MUTE
key <span class="hljs-number">67</span>    VOLUME_DOWN
key <span class="hljs-number">68</span>    VOLUME_UP
key <span class="hljs-number">125</span>   CAPS_LOCK</code></pre>
<p>这里分别简单解释下：</p>
<ul>
<li>59 - 后退键，改成了「上一个节目」键，改成 <code>BACK</code> 后退键等特殊键似乎会导致 Remix OS 下键盘输入有点问题，索性改成切换节目键可以配合各种音乐 App 方便切换歌曲；</li>
<li>60 - 前进键，改成了「下一个节目」键；</li>
<li>61 - 刷新键，改成了「播放/暂停」键，本来想改成 <code>MEDIA_REWIND</code> 也就是「倒回」键符合原来刷新键的意思，但测试后发现好多 App 不支持就算了；</li>
<li>62 - 全屏键，改成了「菜单」键，Remix OS 下没有直接的全屏按键，这里就用做现在比较少用了的「菜单」键了，目前看起来只有手动退出手机 QQ 时有用 ^_^；</li>
<li>63 - 切换窗口键，改成了「应用切换」键，类似 Android 原生的效果，和 Remix OS 自带的按 <kbd>Alt+Tab</kbd> 键切换应用效果稍微有点不一样；</li>
<li>64 - 降低亮度键；</li>
<li>65 - 提高亮度键；</li>
<li>66 - 静音键；</li>
<li>67 - 降低音量键；</li>
<li>68 - 提高音量键；</li>
<li>125 - 搜索键，改成了「大小写切换」键，因为发现现在 Android 下对原生搜索键支持的 App 太少（测试了只有 Chrome 支持），就改成了更常用的 <kbd>Caps lock</kbd> 键了，如果你想换成 Android 原生搜索键的话可以将值改为 <code>SEARCH</code>。</li>
</ul>
<p>修改之后重新启动 Remix OS 系统就可以看到这些特殊按键的效果了，这样起码能让 Chromebook 用的更顺利咯。如果文章中有任何问题欢迎提出指正哦，祝元宵节后玩的开心～～～</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/remixos-cb-keyboard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Chromebook使用ext4 U盘运行Remix OS</title>
		<link>https://zohead.com/archives/cb-ext4-remixos/</link>
		<comments>https://zohead.com/archives/cb-ext4-remixos/#comments</comments>
		<pubDate>Sun, 14 Feb 2016 18:55:23 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Chromebook]]></category>
		<category><![CDATA[ext4]]></category>
		<category><![CDATA[EXTLINUX]]></category>
		<category><![CDATA[Grub]]></category>
		<category><![CDATA[Remix OS]]></category>
		<category><![CDATA[U盘]]></category>
		<category><![CDATA[VMware]]></category>
		<category><![CDATA[桌面]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1167</guid>
		<description><![CDATA[之前在 Dell Chromebook 11 上体验 Remix OS PC 版的时候是直接使用的普通 USB 3.0 U 盘安装官方放出的 Windows 可执行安装版，在 Chromebook 使用 legacy 模式启动（刷了 RW_LEGACY 模式的修改版 BIOS）运行倒是还比较顺利，只是 Remix OS PC 版 U 盘默认 FAT32 分区格式限制了 Android 的用户空间（data 分区）也最多只有 4GB，这样用起来还是挺不爽的。 搜索之后发现 Remix 论坛里已经有直接的硬盘安装方法可以解决 4GB 用户空间限制的问题，但是我的 Chromebook 就只有 16 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>之前在 Dell Chromebook 11 上体验 Remix OS PC 版的时候是直接使用的普通 USB 3.0 U 盘安装官方放出的 Windows 可执行安装版，在 Chromebook 使用 legacy 模式启动（刷了 RW_LEGACY 模式的修改版 BIOS）运行倒是还比较顺利，只是 Remix OS PC 版 U 盘默认 FAT32 分区格式限制了 Android 的用户空间（data 分区）也最多只有 4GB，这样用起来还是挺不爽的。</p>
<p>搜索之后发现 Remix 论坛里已经有直接的硬盘安装方法可以解决 4GB 用户空间限制的问题，但是我的 Chromebook 就只有 16GB 的空间，同时又安装了 crouton 系统，本来就捉襟见肘的空间再给 Remix OS 分一个区就不太现实了，最主要 Chromebook 原生的 Chrome OS 我是必须要保留的，而且 U 盘格式的 Android x86 也比较符合我的需求，因此想到用 Remix OS PC 版里自带的 Android x86 安装器将 Remix OS 安装到 ext4 格式的 U 盘里。</p>
<blockquote>
<p><strong>提示</strong> <br />
  本文中介绍的安装及启动、升级方法不只是适用于 Chromebook 电脑，也同样适用于普通 Windows PC 机； <br />
  另外这篇文章并不是面向初学者的安装教程之类的，需要你对 Linux 有一定的了解。</p>
</blockquote>
<p>下面介绍一下安装方法以及后续升级的步骤：</p>
<h2 id="准备步骤">准备步骤</h2>
<ul>
<li>一个 8GB 以上的 U 盘，最好是 USB 3.0 的；</li>
<li>下载好的 Remix OS PC 版最新<a href="http://www.jide.com/remixos-for-pc">安装映像</a>；</li>
<li>VMware / VirtualBox / QEMU 等支持 USB 模拟的虚拟机软件。</li>
</ul>
<h2 id="vmware-安装-remix-os-步骤">VMware 安装 Remix OS 步骤</h2>
<p>这里以 VMware Workstation 虚拟机软件为例子介绍如何在 Windows 上将 Remix OS PC 版映像以 ext4 文件系统格式安装到普通 U 盘上，其它的虚拟机软件或者操作系统下步骤也是类似的（直接使用 Chromebook 安装 VirtualBox 也是可以完成的哦）。</p>
<ol>
<li>解压缩下载到的 Remix OS PC 版安装映像（一般为 zip 文件），会得到一个比较大的 iso 文件；</li>
<li>创建新虚拟机，记得启用 USB 控制器支持（默认就会启用），然后添加虚拟光驱并使用解压缩出来的 iso 文件，另外不用添加任何虚拟硬盘；</li>
<li>启动虚拟机，正常的话会看到 Android x86 安装画面，这里选择默认的 <code>Resident mode</code> 并按 Tab 键会出现输入参数的界面，在原有的参数后面增加 <code>INSTALL=1</code>，如下图所示：
</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442937/remixos-install.png" alt="Android x86 启动画面" title="Android x86 启动画面"> </p>
</li>
<li>按回车键启动安装程序，稍等就会出现选择安装分区界面：
</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442936/remixos-detectdev.png" alt="选择安装分区" title="选择安装分区"> </p>
</li>
<li>对于 VMware 虚拟机这时候需要将焦点切换到虚拟机中并插上 U 盘，一切正常的话稍等片刻就可以在虚拟机状态栏上看到新插上的 USB 设备：
</p>
<blockquote>
<p><strong>说明</strong> <br />
  对于 VirtualBox 这样可以直接在虚拟机设置中添加 USB 设备的虚拟机软件会在虚拟机启动时自动尝试加载 USB 设备就不需要专门做这步了； <br />
  QEMU 也可以直接通过命令行或者配置文件加载 USB 设备； <br />
  VMware 也是可以通过 vmx 配置文件自动加载 USB 设备的，只是配置稍微复杂点这里不做介绍了。</p>
</blockquote>
<p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442962/vmware-usb-dev.jpg" alt="VMware USB 设备" title="VMware USB 设备"> </p>
</li>
<li>找到 U 盘设备之后选择上面的 <code>Detect devices</code> 菜单项查找设备，顺利的话会看到要安装到的设备：
</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442937/remixos-usb-dev.png" alt="安装 U 盘" title="安装 U 盘"> </p>
</li>
<li>上图可以看到 U 盘上还没有任何分区，下面可以选择 <code>Create/Modify partitions</code> 菜单在 U 盘上创建并格式化分区，分区文件系统格式最好为 ext4 格式，另外最好也直接将整个 U 盘的空间都用上：
</p>
<blockquote>
<p><strong>提示</strong> <br />
  如果 U 盘上已经存在分区但不是 ext4 格式的也可以在这里删除并重新创建分区。</p>
</blockquote>
<p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442934/remixos-create-partition.png" alt="创建分区" title="创建分区"> </p>
</li>
<li>分区创建并格式化完成之后退出返回就可以在之前的选择分区界面中看到新创建的分区：
</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442937/remixos-usb-partition.png" alt="使用新分区" title="使用新分区"> </p>
</li>
<li>选择新创建的分区开始安装，顺利的话稍等几分钟应该就可以安装完成，中间会提示是否需要将 system 分区设为可写的，为了调试以及后续 root 方便建议选择 Yes 将 system 分区设备可写模式。 </li>
<li>这样安装之后的 U 盘就可以一般的电脑上启动成功了，而且用户空间（data 分区）和系统空间（system 分区）都是 ext4 格式的，不存在 4GB 的大小限制，同时后续如果换更大的 U 盘也比较好扩容。</li>
</ol>
<p>安装好之后我们可以看看 Remix OS PC 版 U 盘的分区及文件系统结构：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# parted /dev/sdb p
Model: SanDisk Cruzer Glide 3.0 (scsi)
Disk /dev/sdb: 16.0GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name  标志
 1      1049kB  16.0GB  16.0GB  ext4

[root@localhost ~]# mount -t ext4 /dev/sdb1 /mnt/disk
[root@localhost ~]# ls /mnt/disk/
android-2016-01-23  grub  lost+found
[root@localhost ~]# ls /mnt/disk/android-2016-01-23/
data  initrd.img  kernel  ramdisk.img  system
[root@localhost ~]# ls /mnt/disk/android-2016-01-23/system/
addon  app  bin  build.prop  etc  fonts  framework  lib  lib64  lost+found  media  priv-app  priv-setting  tts  usr  vendor  xbin
[root@localhost ~]# ls /mnt/disk/android-2016-01-23/data/
adb  app-asec  app-private  dalvik-cache  dontpanic  local       media     misc      resource-cache  serialno  user
app  app-lib   bugreports   data          drm        lost+found  mediadrm  property  security        system
[root@localhost ~]# df -h /mnt/disk
文件系统              容量  已用  可用 已用%% 挂载点
/dev/sdb1              15G  1.7G   13G  12% /mnt/disk
</pre>
<p>可以看到 U 盘根目录下就是 android-2016-01-23 这样的目录，里面放的就是 Android kernel、initrd 文件以及实际的 data 及 system 目录，安装完成之后 U 盘实际占用了近 2GB 的空间。</p>
<h2 id="chromebook-u-盘启动问题">Chromebook U 盘启动问题</h2>
<p>下面将安装好的 U 盘在 Dell Chromebook 11 上用 legacy 模式启动时会出现无法正常启动的问题。经过初步研究认为是 Android x86 默认使用的 Trusted Grub 安全引导方式对于刷了 RW_LATENCY 模式 BIOS 的 Chromebook 可能存在不兼容的问题。</p>
<blockquote>
<p>刷了 RW_LATENCY 模式 BIOS 的 Chromebook 进入 legacy 模式的方法是在启动时按 <kbd>Ctrl+L</kbd> 键，并选择启动设备。</p>
</blockquote>
<p>为解决这个问题，我在安装好的 U 盘上尝试安装了 Grub、Grub2 等不同的引导程序发现都不能在 Dell Chromebook 11 上正常启动，最后发现如果改为使用 <a href="http://www.syslinux.org/wiki/index.php/EXTLINUX">EXTLINUX</a> 这个 Syslinux 变种不但能支持 ext4 格式分区引导而且支持在 Chromebook 上以 legacy 方式正常启动 Remix OS U 盘。</p>
<p>EXTLINUX 安装也比较简单，这里直接使用 Chromebook 中的 crouton 系统直接安装到 U 盘，下面的操作都需要使用 root 权限才能完成。</p>
<ul>
<li>首先挂载 U 盘分区，并在分区下创建 extlinux 目录，然后运行命令安装 EXTLINUX 相关文件到对应目录：
</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mkdir -p /mnt/disk/extlinux
[root@localhost ~]# extlinux --install /mnt/disk/extlinux
</pre>
</li>
<li>生成 EXTLINUX 配置文件保存到 <code>extlinux/extlinux.conf</code> 中：
</p>
<pre class="brush: bash; title: ; notranslate">
TIMEOUT 5
DEFAULT Remix_OS_2016-01-23

LABEL Remix_OS_2016-01-23
	KERNEL /android-2016-01-23/kernel
	APPEND quiet root=/dev/ram0 androidboot.hardware=remix_cn_x86_64 androidboot.selinux=permissive DATA= SRC=/android-2016-01-23 initrd=/android-2016-01-23/initrd.img

LABEL Remix_OS_2016-01-23_Debug
	KERNEL /android-2016-01-23/kernel
	APPEND root=/dev/ram0 androidboot.hardware=remix_cn_x86_64 androidboot.selinux=permissive DATA= DEBUG=2 SRC=/android-2016-01-23 initrd=/android-2016-01-23/initrd.img
</pre>
<p>其中实际标题及目录名称请根据需要自行替换； </p>
</li>
<li>安装 EXTLINUX 引导程序到 U 盘：
</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# umount /mnt/disk
[root@localhost ~]# cat /usr/lib/extlinux/mbr.bin &gt; /dev/sdb
</pre>
<blockquote>
<p><strong>注意</strong> <br />
  进行此步骤还请特别注意不要搞错了 U 盘设备名，否则可能误安装到你的系统盘之类的哦。</p>
</blockquote>
</li>
</ul>
<p>一般这样修改之后的 U 盘就可以正常在 Chromebook 上以 legacy 模式启动了，这种方式最大的优点就是不影响 Chromebook 原生的 Chrome OS 系统。</p>
<h2 id="remix-os-系统升级">Remix OS 系统升级</h2>
<p>由于 Remix OS PC 版并没有提供直接的在线 OTA 升级方式，但按本文提供的方式安装的 U 盘明显又不能使用官方的 Windows 安装工具进行升级，这里就需要专门介绍一下如何升级新系统了。</p>
<p>其实步骤也算是比较简单的，只需要下载新的映像文件解压缩 iso 里的文件，并使用新的 kernel、ramdisk、initrd 替换到对应目录，然后替换整个 system 目录即可（保留 data 目录就可以保留所有用户数据）。</p>
<p>首先挂载 U 盘分区到指定目录，例如 <code>/mnt/disk</code>，上面已经有具体的命令这里不再说明了。</p>
<p>然后挂载 iso 中的 system 文件，这一步由于 Android x86 system 目录使用的是 squashfs 映像所以略微复杂点：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/Downloads$ sudo mount -t iso9660 -o ro,loop Remix_OS_for_PC_64_B2016020201_CN_Alpha_Legacy.iso /mnt/test
(trusty)zzm@localhost:~/Downloads$ ls /mnt/test/
efi/         initrd.img   install.img  isolinux/    kernel       ramdisk.img  system.sfs   TRANS.TBL
(trusty)zzm@localhost:~/Downloads$ sudo mount -t squashfs -o ro,loop /mnt/test/system.sfs /mnt/image
(trusty)zzm@localhost:~/Downloads$ ls /mnt/image/
system.img
(trusty)zzm@localhost:~/Downloads$ sudo mount -t ext4 -o ro,loop /mnt/image/system.img /mnt/share
(trusty)zzm@localhost:~/Downloads$ ls /mnt/share/
addon  app  bin  build.prop  etc  fonts  framework  lib  lib64  lost+found  media  priv-app  priv-setting  tts  usr  vendor  xbin
</pre>
<p>接着就是将新的 system 目录到 U 盘中（下面的命令开始多了一条删除 U 盘中原来所有 system 文件的操作）：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/Downloads$ sudo rm -rf /mnt/disk/android-2016-01-23/system/*
(trusty)zzm@localhost:~/Downloads$ cp -ra /mnt/share/* /mnt/disk/android-2016-01-23/system/
</pre>
<p>下面是替换 Android kernel、initrd、ramdisk 等文件：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/Downloads$ sudo cp /mnt/test/initrd.img /mnt/test/kernel /mnt/test/ramdisk.img /mnt/disk/android-2016-01-23/
</pre>
<p>最后可以将原来的 Android 目录（例如上面的 android-2016-01-23）也重命名为最新的目录（例如：android-2016-02-02），不过重命名之后千万别忘了把 extlinux.conf 配置文件里的标题和目录名称也改掉哦。</p>
<p>依次卸载上面的一些挂载之后用新的 U 盘启动 Chromebook 就可以看到升级后的效果啦。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/cb-ext4-remixos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android使用Trigger+Tasker设定NFC标签</title>
		<link>https://zohead.com/archives/tasker-nfc/</link>
		<comments>https://zohead.com/archives/tasker-nfc/#comments</comments>
		<pubDate>Tue, 03 Nov 2015 18:14:53 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[GPS]]></category>
		<category><![CDATA[NFC]]></category>
		<category><![CDATA[Secure Settings]]></category>
		<category><![CDATA[Sleep as Android]]></category>
		<category><![CDATA[Tasker]]></category>
		<category><![CDATA[Trigger]]></category>
		<category><![CDATA[飞行模式]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=1065</guid>
		<description><![CDATA[现在用的华硕 New PadFone Infinity A86 手机一直都是支持 NFC 功能的，只是之前一直没怎么用过，基本只有在查询公交卡余额的时候才用一下（这里顺便安利一下南京市政府组织开发的 “我的南京” App，可以通过手机 NFC 查询公交卡余额），最近将手机 ROM 升级到 Android 5.0 之后终于觉得 5.0 自带的 Smart Lock 功能会比较适合我了，可以使用蓝牙、NFC 标签等实现手机智能解锁功能。 升级 Android 5.0 另外一个方便的地方就是我的 Chromebook 也支持 Smart Lock 功能，两边都开启之后如果 Android 5.0 以 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>现在用的华硕 New PadFone Infinity A86 手机一直都是支持 NFC 功能的，只是之前一直没怎么用过，基本只有在查询公交卡余额的时候才用一下（这里顺便安利一下南京市政府组织开发的 “我的南京” App，可以通过手机 NFC 查询公交卡余额），最近将手机 ROM 升级到 Android 5.0 之后终于觉得 5.0 自带的 Smart Lock 功能会比较适合我了，可以使用蓝牙、NFC 标签等实现手机智能解锁功能。</p>
<p>升级 Android 5.0 另外一个方便的地方就是我的 Chromebook 也支持 Smart Lock 功能，两边都开启之后如果 Android 5.0 以上的手机在 Chromebook 旁边并且已经解锁，那 Chromebook 也可以自动解锁无需再输入密码登录。</p>
<p>经过一些研究之后，还是买了一些 NFC 标签，准备用之前就捣鼓过的 <a href="https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm" target="_blank">Tasker</a> App（收费 2.99 美元）实现 NFC 标签的各种触发操作，不过这还需要配合使用 <a href="https://play.google.com/store/apps/details?id=com.jwsoft.nfcactionlauncher" target="_blank">Trigger</a> App（基本功能免费，增强功能通过应用内购买），因为 Tasker 没有直接支持 NFC 触发条件，Trigger 倒是有在检测到 NFC 标签之后执行 Tasker 任务的功能，需要特别注意的是 Trigger 必须在安装好 Tasker 之后安装。</p>
<p>Trigger 的 NFC 标签功能具体如何操作也比较简单这里就不介绍了，只需要在执行的动作里勾选 “Tasker” - “Tasker任务” 并选择实际需要执行的 Tasker 任务即可，这里我以两个实际例子说明 Tasker 如何实现稍微复杂一些的任务。</p>
<p><strong>1、一个 NFC 标签实现自动开启停止睡眠追踪：</strong></p>
<p><span style="color: #000080;"><em>需求：</em></span></p>
<p>平时我会使用 <a href="https://play.google.com/store/apps/details?id=com.urbandroid.sleep" target="_blank">Sleep as Android</a> 软件（免费试用 14 天）监控睡眠状况，其功能非常完善而且实际使用起来也很方便（虽然可能不会非常准确 ～～ 哈哈），只是这个 App 是一直在前台运行的比较耗电，而且每天晚上睡觉之前就需要手工开启，早上醒来之后再手工停止，因此就想到在床头贴上一个 NFC 标签实现睡觉之前刷一下自动启动睡眠追踪并开启飞行模式以省电，早上起来之后再刷一下这个 NFC 标签可以停止睡眠追踪并自动关闭飞行模式。</p>
<p><span style="color: #000080;"><em>问题：</em></span></p>
<p>幸好 Sleep as Android 是自带 Tasker 插件，可以在添加任务时选择插件，并指定 “睡眠追踪”，之后就能配置具体执行哪种操作（启动或者停止睡眠追踪之类的）；</p>
<p>另外飞行模式的切换在新的 Android 系统中已经不能直接修改了，这时需要祭出大杀器 <a href="https://play.google.com/store/apps/details?id=com.intangibleobject.securesettings.plugin" target="_blank">Secure Settings</a> App 了，安装之后也会自动增加一个 Secure Settings Tasker 插件，不过后面的操作要求手机必须 root，root 之后启用 Secure Settings 的 System+ Module，这样将 GPS、飞行模式之类的控制功能安装到系统分区就能控制飞行模式之类的切换了。</p>
<p><span style="color: #000080;"><em>Tasker 任务配置：</em></span></p>
<ul>
<li>睡眠追踪 Tasker 插件 - 开始 睡眠追踪，并设置条件：<strong>%AIR = off</strong>；</li>
<li>Secure Settings Tasker 插件 - Airplane Mode Enabled，并设置条件：<strong>%AIR = off</strong>；</li>
<li>睡眠追踪 Tasker 插件 - 停止 睡眠追踪，并设置条件：<strong>%AIR = on</strong>；</li>
<li>Secure Settings Tasker 插件 - Airplane Mode Disabled，并设置条件：<strong>%AIR = on</strong>；</li>
<li>等待 - 3 秒；</li>
<li>Secure Settings Tasker 插件 - NFC Enabled，并设置条件：<strong>%AIR = on</strong>。</li>
</ul>
<p>上面的 5 个步骤顺序不能搞错，而且对应的条件也很重要。</p>
<p><span style="color: #000080;"><em>机制解释：</em></span></p>
<p>第 1 个任务先判断如果当前手机飞行模式（Tasker 中以 %AIR 变量表示）是关闭的，则认为要开始睡眠追踪，接着第 2 个任务在同样的条件下就通过 Secure Settings 启用飞行模式，第 3 个任务则是发现如果飞行模式已启用则认为已经是在睡眠追踪状态，这时就需要停止追踪，第 4 个任务类似跟着就是关闭飞行模式；</p>
<p>第 5 个和第 6 个任务稍微特殊一点，因为我们在启用飞行模式之后 NFC 功能也同样会被禁用的，这样就没法在早上醒来时刷 NFC 标签了，这样就需要在前面几步都做好之后先等待几秒钟，然后判断如果飞行模式已开启则通过 Secure Settings 自动再将 NFC 功能也开启，而且这两个任务不能直接在第 2 个任务（开启飞行模式）之后就执行，因为开启飞行模式之后 %AIR 得到的飞行模式状态不是马上就能更新过来。</p>
<p><strong>2、一个 NFC 标签实现自动开启关闭百度地图：</strong></p>
<p><span style="color: #000080;"><em>需求：</em></span></p>
<p>这个需求就比较常见了，在我的 SUV 汽车仪表盘附近贴一个百度地图导航专用 NFC 标签，实现刷一下自动开启 GPS 并启动百度地图导航，等导航结束了再刷一下就自动退出百度地图并关闭 GPS。</p>
<p><em><span style="color: #000080;">问题：</span></em></p>
<p>开启关闭 GPS 的问题和飞行模式类似，还是通过 Secure Settings 插件来实现，Tasker 直接支持启动其它 App 的操作，也支持杀死其它 App。</p>
<p><span style="color: #000080;"><em>Tasker 任务配置：</em></span></p>
<ul>
<li>Secure Settings Tasker 插件 - GPS Enabled，并设置条件：<strong>%GPS = off</strong>；</li>
<li>启动应用 - 百度地图，并设置条件：<strong>%GPS = off</strong>；</li>
<li>按钮 - 后退（此任务重复添加 5 次或以上），并设置同样的条件：<strong>%GPS = on</strong>；</li>
<li>杀死应用 - 百度地图，并设置条件：<strong>%GPS = on</strong>；</li>
<li>Secure Settings Tasker 插件 - GPS Disabled，并设置条件：<strong>%GPS = on</strong>。</li>
</ul>
<p><span style="color: #000080;"><em>机制解释：</em></span></p>
<p>具体机制还是和上面比较类似的，使用 Tasker 内置的 %GPS 变量根据当前 GPS 状态执行对应的任务操作，值得一提的是第 3 个任务这里为了优雅一点我先用发送多次返回按键的方式模拟退出百度地图的操作，当然对于百度全家桶系的手机 App 我是很乐意直接杀死禁止后台服务和唤醒之类的，因此倒数第二步我还是加上了杀掉百度地图的任务 ^_^，最后再关闭 GPS。</p>
<p>有一点需要说明的是由于 Tasker 没有直接判断某个 App 是否在前台运行的方法（虽然可以另外安装 <a href="https://play.google.com/store/apps/details?id=org.kc.tasker.processes" target="_blank">Tasker Process Running</a> 这类插件判断某个 App 是否在运行，但其不好区分前台后台，百度地图这种在后台运行的情况不好处理），因此这两个例子里我都是直接用 Tasker 自带的飞行模式或 GPS 当前状态判断要执行什么操作的。</p>
<p>总之使用 Tasker 配合 NFC 标签并安装需要的插件还是能实现各种特殊的功能滴，本文中使用的所有 App 的链接都是 Google Play 官方地址，国内用户可以自行访问国内各种手机市场下载安装，祝玩的开心～～～</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/tasker-nfc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
