Skip to content

Commit 3586577

Browse files
committed
add Xposed hook for android nfc service
update AGP version
1 parent 8fcbc96 commit 3586577

File tree

9 files changed

+249
-1
lines changed

9 files changed

+249
-1
lines changed

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ dependencies {
8989
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
9090
implementation 'com.google.code.gson:gson:2.8.9'
9191
implementation 'androidx.preference:preference-ktx:1.1.1'
92+
compileOnly("de.robv.android.xposed:api:82")
9293
testImplementation 'junit:junit:4.13.2'
9394
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
9495
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

app/src/main/AndroidManifest.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,19 @@
147147
</intent-filter>
148148
</receiver>
149149

150+
<meta-data
151+
android:name="xposedmodule"
152+
android:value="true" />
153+
<meta-data
154+
android:name="xposeddescription"
155+
android:value="@string/xposeddescription" />
156+
<meta-data
157+
android:name="xposedminversion"
158+
android:value="51" />
159+
<meta-data
160+
android:name="xposedscope"
161+
android:resource="@array/xposedscope" />
162+
150163
<meta-data
151164
android:name="com.sec.android.support.multiwindow"
152165
android:value="true" />

app/src/main/assets/xposed_init

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cc.ioctl.nfcdevicehost.xposed.HookEntry
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cc.ioctl.nfcdevicehost.xposed;
2+
3+
import de.robv.android.xposed.IXposedHookLoadPackage;
4+
import de.robv.android.xposed.XC_MethodHook;
5+
import de.robv.android.xposed.XposedHelpers;
6+
import de.robv.android.xposed.callbacks.XC_LoadPackage;
7+
8+
public class HookEntry implements IXposedHookLoadPackage {
9+
10+
private static void initNfcServiceHook(ClassLoader classLoader) {
11+
XposedHelpers.findAndHookMethod("com.android.nfc.NfcService", classLoader, "playSound", int.class, new XC_MethodHook() {
12+
@Override
13+
protected void beforeHookedMethod(MethodHookParam param) {
14+
if (SysNfcSvcPatch.sDisableNfcDiscoverySound) {
15+
param.setResult(null);
16+
}
17+
}
18+
});
19+
SysNfcSvcPatch.startMainThread();
20+
}
21+
22+
@Override
23+
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
24+
if ("com.android.nfc".equals(lpparam.packageName)) {
25+
if (lpparam.isFirstApplication) {
26+
initNfcServiceHook(lpparam.classLoader);
27+
}
28+
}
29+
}
30+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package cc.ioctl.nfcdevicehost.xposed;
2+
3+
import android.net.Credentials;
4+
import android.net.LocalServerSocket;
5+
import android.net.LocalSocket;
6+
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.OutputStream;
10+
11+
import de.robv.android.xposed.XposedBridge;
12+
13+
public class SysNfcSvcPatch {
14+
15+
private static Thread sWorkingThread = null;
16+
public static boolean sDisableNfcDiscoverySound = false;
17+
private static boolean sShouldRunning = true;
18+
19+
static void run() {
20+
try {
21+
int myPid = android.os.Process.myPid();
22+
while (sShouldRunning) {
23+
LocalServerSocket localServerSocket;
24+
try {
25+
localServerSocket = new LocalServerSocket("com.android.nfc/cc.ioctl.nfcdevicehost.xposed.SysNfcSvcPatch@" + myPid);
26+
while (sShouldRunning) {
27+
LocalSocket localSocket = localServerSocket.accept();
28+
Credentials cred = null;
29+
try {
30+
cred = localSocket.getPeerCredentials();
31+
} catch (IOException ignored) {
32+
}
33+
if (cred != null && cred.getUid() == 0) {
34+
localServerSocket.close();
35+
handleTransaction(localSocket);
36+
// socket is closed in handleTransaction
37+
break;
38+
} else {
39+
try {
40+
localSocket.close();
41+
} catch (IOException ignored) {
42+
}
43+
}
44+
}
45+
} catch (IOException e) {
46+
XposedBridge.log("SysNfcSvcPatch: " + e.getMessage());
47+
XposedBridge.log(e);
48+
return;
49+
}
50+
}
51+
} catch (RuntimeException e) {
52+
XposedBridge.log(e);
53+
}
54+
}
55+
56+
static final int TYPE_REQUEST = 1;
57+
static final int TYPE_RESPONSE = 2;
58+
static final int TYPE_EVENT = 3;
59+
static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
60+
61+
static final int REQUEST_SET_NFC_SOUND_DISABLE = 0x30;
62+
static final int REQUEST_GET_NFC_SOUND_DISABLE = 0x31;
63+
64+
static void handleTransaction(LocalSocket socket) {
65+
try {
66+
byte[] buffer = new byte[65536];
67+
InputStream is = socket.getInputStream();
68+
while (sShouldRunning) {
69+
// read 4 bytes length
70+
readFully(is, buffer, 0, 4);
71+
int length = getInt32Le(buffer, 0);
72+
if (length < 16 || length > 65535) {
73+
break;
74+
}
75+
// read length bytes
76+
readFully(is, buffer, 0, length);
77+
// uint32_t type
78+
int type = getInt32Le(buffer, 0);
79+
// uint32_t sequence
80+
int sequence = getInt32Le(buffer, 4);
81+
// uint32_t requestCode
82+
int requestCode = getInt32Le(buffer, 8);
83+
// uint32_t arg0
84+
int arg0 = getInt32Le(buffer, 12);
85+
byte[] data;
86+
if (length > 16) {
87+
data = new byte[length - 16];
88+
System.arraycopy(buffer, 16, data, 0, length - 16);
89+
} else {
90+
data = EMPTY_BYTE_ARRAY;
91+
}
92+
switch (type) {
93+
case TYPE_REQUEST:
94+
handleRequest(socket, sequence, requestCode, arg0, data);
95+
break;
96+
case TYPE_RESPONSE:
97+
case TYPE_EVENT:
98+
default:
99+
break;
100+
}
101+
}
102+
} catch (IOException e) {
103+
XposedBridge.log("SysNfcSvcPatch: " + e.getMessage());
104+
}
105+
try {
106+
socket.close();
107+
} catch (IOException ignored) {
108+
}
109+
}
110+
111+
private static void handleRequest(LocalSocket socket, int sequence, int requestCode, int arg0, byte[] data) {
112+
switch (requestCode) {
113+
case REQUEST_SET_NFC_SOUND_DISABLE: {
114+
sDisableNfcDiscoverySound = (arg0 != 0);
115+
sendResponse(socket, sequence, 0, 0, null);
116+
break;
117+
}
118+
case REQUEST_GET_NFC_SOUND_DISABLE: {
119+
sendResponse(socket, sequence, sDisableNfcDiscoverySound ? 1 : 0, 0, null);
120+
break;
121+
}
122+
default: {
123+
sendResponse(socket, sequence, -1, -1, null);
124+
}
125+
}
126+
}
127+
128+
static void sendResponse(LocalSocket socket, int sequence, int resultCode, int errCode, byte[] data) {
129+
if (data == null) {
130+
data = EMPTY_BYTE_ARRAY;
131+
}
132+
byte[] bufferLen4 = new byte[4];
133+
putInt32Le(bufferLen4, 0, data.length + 16);
134+
byte[] buffer = new byte[16 + data.length];
135+
putInt32Le(buffer, 0, TYPE_RESPONSE);
136+
putInt32Le(buffer, 4, sequence);
137+
putInt32Le(buffer, 8, resultCode);
138+
putInt32Le(buffer, 12, errCode);
139+
System.arraycopy(data, 0, buffer, 16, data.length);
140+
try {
141+
OutputStream os = socket.getOutputStream();
142+
os.write(bufferLen4);
143+
os.write(buffer);
144+
os.flush();
145+
} catch (IOException e) {
146+
XposedBridge.log("SysNfcSvcPatch: " + e.getMessage());
147+
}
148+
}
149+
150+
public static synchronized void startMainThread() {
151+
if (sWorkingThread == null || !sWorkingThread.isAlive()) {
152+
sWorkingThread = new Thread(SysNfcSvcPatch::run);
153+
sWorkingThread.start();
154+
}
155+
}
156+
157+
static void readFully(InputStream is, byte[] buffer, int offset, int length) throws IOException {
158+
int remaining = length;
159+
while (remaining > 0) {
160+
int read = is.read(buffer, offset, remaining);
161+
if (read < 0) {
162+
throw new IOException("Unexpected EOF");
163+
}
164+
remaining -= read;
165+
offset += read;
166+
}
167+
}
168+
169+
/**
170+
* Read a little-endian 32-bit integer from the given byte array.
171+
*
172+
* @param buffer The byte array to read from.
173+
* @param offset The offset to start reading from.
174+
* @return The 32-bit integer read.
175+
*/
176+
static int getInt32Le(byte[] buffer, int offset) {
177+
return (buffer[offset] & 0xFF) | ((buffer[offset + 1] & 0xFF) << 8)
178+
| ((buffer[offset + 2] & 0xFF) << 16) | ((buffer[offset + 3] & 0xFF) << 24);
179+
}
180+
181+
/**
182+
* Put a little-endian 32-bit integer to the given byte array.
183+
*
184+
* @param buffer The byte array to write to.
185+
* @param offset The offset to start writing to.
186+
* @param value the value to put
187+
*/
188+
static void putInt32Le(byte[] buffer, int offset, int value) {
189+
buffer[offset] = (byte) (value & 0xFF);
190+
buffer[offset + 1] = (byte) ((value >> 8) & 0xFF);
191+
buffer[offset + 2] = (byte) ((value >> 16) & 0xFF);
192+
buffer[offset + 3] = (byte) ((value >> 24) & 0xFF);
193+
}
194+
}

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@ NCI Host 的开发人员不对使用此应用程序造成的任何损失负责
7070
<string name="action_decoder_config">解码选项</string>
7171
<string name="ui_hal_dump_decoder_config_title">解码选项</string>
7272
<string name="action_confirm">确认</string>
73+
<string name="xposeddescription">NFC 系统服务附加功能</string>
7374

7475
</resources>

app/src/main/res/values/arrays.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
<item>reply</item>
1010
<item>reply_all</item>
1111
</string-array>
12+
1213
<string-array name="ui_hal_dump_decoder_config_items">
1314
<item>None(raw syscall dump)</item>
1415
<item>NCI protocol</item>
1516
</string-array>
17+
18+
<string-array name="xposedscope">
19+
<item>com.android.nfc</item>
20+
</string-array>
21+
1622
</resources>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,5 @@ By using this application, you accept all responsibility using it and agree that
9999
<string name="action_decoder_config">Decoder properties</string>
100100
<string name="ui_hal_dump_decoder_config_title">Decoder Properties</string>
101101
<string name="action_confirm">Confirm</string>
102+
<string name="xposeddescription">Extra features for NFC service.</string>
102103
</resources>

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ buildscript {
77
mavenCentral()
88
}
99
dependencies {
10-
classpath 'com.android.tools.build:gradle:7.0.3'
10+
classpath 'com.android.tools.build:gradle:7.0.4'
1111
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1212
// NOTE: Do not place your application dependencies here; they belong
1313
// in the individual module build.gradle files
@@ -19,6 +19,7 @@ allprojects {
1919
google()
2020
mavenCentral()
2121
maven { url 'https://jitpack.io' }
22+
maven { url 'https://api.xposed.info/' }
2223
}
2324
}
2425

0 commit comments

Comments
 (0)