1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "UsbDeviceManagerJNI"
18 #include <android-base/properties.h>
19 #include <android-base/unique_fd.h>
20 #include <core_jni_helpers.h>
21 #include <fcntl.h>
22 #include <linux/usb/f_accessory.h>
23 #include <nativehelper/JNIPlatformHelp.h>
24 #include <nativehelper/ScopedUtfChars.h>
25 #include <stdio.h>
26 #include <sys/epoll.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30
31 #include <thread>
32
33 #include "MtpDescriptors.h"
34 #include "android_runtime/AndroidRuntime.h"
35 #include "android_runtime/Log.h"
36 #include "jni.h"
37 #include "utils/Log.h"
38
39 #define DRIVER_NAME "/dev/usb_accessory"
40 #define EPOLL_MAX_EVENTS 4
41 #define USB_STATE_MAX_LEN 20
42
43 namespace android
44 {
45
46 static JavaVM *gvm = nullptr;
47 static jmethodID gUpdateGadgetStateMethod;
48
49 static struct parcel_file_descriptor_offsets_t
50 {
51 jclass mClass;
52 jmethodID mConstructor;
53 } gParcelFileDescriptorOffsets;
54
55 /*
56 * NativeGadgetMonitorThread starts a new thread to monitor udc state by epoll,
57 * convert and update the state to UsbDeviceManager.
58 */
59 class NativeGadgetMonitorThread {
60 android::base::unique_fd mMonitorFd;
61 int mPipefd[2];
62 std::thread mThread;
63 jobject mCallbackObj;
64 std::string mGadgetState;
65
handleStateUpdate(const char * state)66 void handleStateUpdate(const char *state) {
67 JNIEnv *env = AndroidRuntime::getJNIEnv();
68 std::string gadgetState;
69
70 if (!std::strcmp(state, "not attached\n")) {
71 gadgetState = "DISCONNECTED";
72 } else if (!std::strcmp(state, "attached\n") || !std::strcmp(state, "powered\n") ||
73 !std::strcmp(state, "default\n") || !std::strcmp(state, "addressed\n")) {
74 gadgetState = "CONNECTED";
75 } else if (!std::strcmp(state, "configured\n")) {
76 gadgetState = "CONFIGURED";
77 } else if (!std::strcmp(state, "suspended\n")) {
78 return;
79 } else {
80 ALOGE("Unknown gadget state %s", state);
81 return;
82 }
83
84 if (mGadgetState.compare(gadgetState)) {
85 mGadgetState = gadgetState;
86 jstring obj = env->NewStringUTF(gadgetState.c_str());
87 env->CallVoidMethod(mCallbackObj, gUpdateGadgetStateMethod, obj);
88 }
89 }
90
setupEpoll(android::base::unique_fd & epollFd)91 int setupEpoll(android::base::unique_fd &epollFd) {
92 struct epoll_event ev;
93
94 ev.data.fd = mMonitorFd.get();
95 ev.events = EPOLLPRI;
96 if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mMonitorFd.get(), &ev) != 0) {
97 ALOGE("epoll_ctl failed for monitor fd; errno=%d", errno);
98 return errno;
99 }
100
101 ev.data.fd = mPipefd[0];
102 ev.events = EPOLLIN;
103 if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mPipefd[0], &ev) != 0) {
104 ALOGE("epoll_ctl failed for pipe fd; errno=%d", errno);
105 return errno;
106 }
107
108 return 0;
109 }
110
monitorLoop()111 void monitorLoop() {
112 android::base::unique_fd epollFd(epoll_create(EPOLL_MAX_EVENTS));
113 if (epollFd.get() == -1) {
114 ALOGE("epoll_create failed; errno=%d", errno);
115 return;
116 }
117 if (setupEpoll(epollFd) != 0) return;
118
119 JNIEnv *env = nullptr;
120 JavaVMAttachArgs aargs = {JNI_VERSION_1_4, "NativeGadgetMonitorThread", nullptr};
121 if (gvm->AttachCurrentThread(&env, &aargs) != JNI_OK || env == nullptr) {
122 ALOGE("Couldn't attach thread");
123 return;
124 }
125
126 struct epoll_event events[EPOLL_MAX_EVENTS];
127 int nevents = 0;
128 while (true) {
129 nevents = epoll_wait(epollFd.get(), events, EPOLL_MAX_EVENTS, -1);
130 if (nevents < 0) {
131 ALOGE("usb epoll_wait failed; errno=%d", errno);
132 continue;
133 }
134 for (int i = 0; i < nevents; ++i) {
135 int fd = events[i].data.fd;
136 if (fd == mPipefd[0]) {
137 goto exit;
138 } else if (fd == mMonitorFd.get()) {
139 char state[USB_STATE_MAX_LEN] = {0};
140 lseek(fd, 0, SEEK_SET);
141 read(fd, &state, USB_STATE_MAX_LEN);
142 handleStateUpdate(state);
143 }
144 }
145 }
146
147 exit:
148 auto res = gvm->DetachCurrentThread();
149 ALOGE_IF(res != JNI_OK, "Couldn't detach thread");
150 return;
151 }
152
stop()153 void stop() {
154 if (mThread.joinable()) {
155 int c = 'q';
156 write(mPipefd[1], &c, 1);
157 mThread.join();
158 }
159 }
160
161 DISALLOW_COPY_AND_ASSIGN(NativeGadgetMonitorThread);
162
163 public:
NativeGadgetMonitorThread(jobject obj,android::base::unique_fd monitorFd)164 explicit NativeGadgetMonitorThread(jobject obj, android::base::unique_fd monitorFd)
165 : mMonitorFd(std::move(monitorFd)), mGadgetState("") {
166 mCallbackObj = AndroidRuntime::getJNIEnv()->NewGlobalRef(obj);
167 pipe(mPipefd);
168 mThread = std::thread(&NativeGadgetMonitorThread::monitorLoop, this);
169 }
170
~NativeGadgetMonitorThread()171 ~NativeGadgetMonitorThread() {
172 stop();
173 close(mPipefd[0]);
174 close(mPipefd[1]);
175 AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mCallbackObj);
176 }
177 };
178 static std::unique_ptr<NativeGadgetMonitorThread> sGadgetMonitorThread;
179
set_accessory_string(JNIEnv * env,int fd,int cmd,jobjectArray strArray,int index)180 static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index)
181 {
182 char buffer[256];
183
184 buffer[0] = 0;
185 ioctl(fd, cmd, buffer);
186 if (buffer[0]) {
187 jstring obj = env->NewStringUTF(buffer);
188 env->SetObjectArrayElement(strArray, index, obj);
189 env->DeleteLocalRef(obj);
190 }
191 }
192
193
android_server_UsbDeviceManager_getAccessoryStrings(JNIEnv * env,jobject)194 static jobjectArray android_server_UsbDeviceManager_getAccessoryStrings(JNIEnv *env,
195 jobject /* thiz */)
196 {
197 int fd = open(DRIVER_NAME, O_RDWR);
198 if (fd < 0) {
199 ALOGE("could not open %s", DRIVER_NAME);
200 return NULL;
201 }
202 jclass stringClass = env->FindClass("java/lang/String");
203 jobjectArray strArray = env->NewObjectArray(6, stringClass, NULL);
204 if (!strArray) goto out;
205 set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0);
206 set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1);
207 set_accessory_string(env, fd, ACCESSORY_GET_STRING_DESCRIPTION, strArray, 2);
208 set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);
209 set_accessory_string(env, fd, ACCESSORY_GET_STRING_URI, strArray, 4);
210 set_accessory_string(env, fd, ACCESSORY_GET_STRING_SERIAL, strArray, 5);
211
212 out:
213 close(fd);
214 return strArray;
215 }
216
android_server_UsbDeviceManager_openAccessory(JNIEnv * env,jobject)217 static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobject /* thiz */)
218 {
219 int fd = open(DRIVER_NAME, O_RDWR);
220 if (fd < 0) {
221 ALOGE("could not open %s", DRIVER_NAME);
222 return NULL;
223 }
224 jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
225 if (fileDescriptor == NULL) {
226 close(fd);
227 return NULL;
228 }
229 return env->NewObject(gParcelFileDescriptorOffsets.mClass,
230 gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
231 }
232
android_server_UsbDeviceManager_isStartRequested(JNIEnv *,jobject)233 static jboolean android_server_UsbDeviceManager_isStartRequested(JNIEnv* /* env */,
234 jobject /* thiz */)
235 {
236 int fd = open(DRIVER_NAME, O_RDWR);
237 if (fd < 0) {
238 ALOGE("could not open %s", DRIVER_NAME);
239 return false;
240 }
241 int result = ioctl(fd, ACCESSORY_IS_START_REQUESTED);
242 close(fd);
243 return (result == 1);
244 }
245
android_server_UsbDeviceManager_openControl(JNIEnv * env,jobject,jstring jFunction)246 static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
247 ScopedUtfChars function(env, jFunction);
248 bool ptp = false;
249 int fd = -1;
250 if (!strcmp(function.c_str(), "ptp")) {
251 ptp = true;
252 }
253 if (!strcmp(function.c_str(), "mtp") || ptp) {
254 fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
255 if (fd < 0) {
256 ALOGE("could not open control for %s %s", function.c_str(), strerror(errno));
257 return NULL;
258 }
259 if (!writeDescriptors(fd, ptp)) {
260 close(fd);
261 return NULL;
262 }
263 }
264
265 jobject jifd = jniCreateFileDescriptor(env, fd);
266 if (jifd == NULL) {
267 // OutOfMemoryError will be pending.
268 close(fd);
269 }
270 return jifd;
271 }
272
android_server_UsbDeviceManager_startGadgetMonitor(JNIEnv * env,jobject thiz,jstring jUdcName)273 static jboolean android_server_UsbDeviceManager_startGadgetMonitor(JNIEnv *env, jobject thiz,
274 jstring jUdcName) {
275 std::string filePath;
276 ScopedUtfChars udcName(env, jUdcName);
277
278 filePath = "/sys/class/udc/" + std::string(udcName.c_str()) + "/state";
279 android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY));
280
281 if (fd.get() == -1) {
282 ALOGE("Cannot open %s", filePath.c_str());
283 return JNI_FALSE;
284 }
285
286 ALOGI("Start monitoring %s", filePath.c_str());
287 sGadgetMonitorThread.reset(new NativeGadgetMonitorThread(thiz, std::move(fd)));
288
289 return JNI_TRUE;
290 }
291
android_server_UsbDeviceManager_stopGadgetMonitor(JNIEnv * env,jobject)292 static void android_server_UsbDeviceManager_stopGadgetMonitor(JNIEnv *env, jobject /* thiz */) {
293 sGadgetMonitorThread.reset();
294 return;
295 }
296
android_server_UsbDeviceManager_waitAndGetProperty(JNIEnv * env,jobject thiz,jstring jPropName)297 static jstring android_server_UsbDeviceManager_waitAndGetProperty(JNIEnv *env, jobject thiz,
298 jstring jPropName) {
299 ScopedUtfChars propName(env, jPropName);
300 std::string propValue;
301
302 while (!android::base::WaitForPropertyCreation(propName.c_str()));
303 propValue = android::base::GetProperty(propName.c_str(), "" /* default */);
304
305 return env->NewStringUTF(propValue.c_str());
306 }
307
308 static const JNINativeMethod method_table[] = {
309 {"nativeGetAccessoryStrings", "()[Ljava/lang/String;",
310 (void *)android_server_UsbDeviceManager_getAccessoryStrings},
311 {"nativeOpenAccessory", "()Landroid/os/ParcelFileDescriptor;",
312 (void *)android_server_UsbDeviceManager_openAccessory},
313 {"nativeIsStartRequested", "()Z", (void *)android_server_UsbDeviceManager_isStartRequested},
314 {"nativeOpenControl", "(Ljava/lang/String;)Ljava/io/FileDescriptor;",
315 (void *)android_server_UsbDeviceManager_openControl},
316 {"nativeStartGadgetMonitor", "(Ljava/lang/String;)Z",
317 (void *)android_server_UsbDeviceManager_startGadgetMonitor},
318 {"nativeStopGadgetMonitor", "()V",
319 (void *)android_server_UsbDeviceManager_stopGadgetMonitor},
320 {"nativeWaitAndGetProperty", "(Ljava/lang/String;)Ljava/lang/String;",
321 (void *)android_server_UsbDeviceManager_waitAndGetProperty},
322 };
323
register_android_server_UsbDeviceManager(JavaVM * vm,JNIEnv * env)324 int register_android_server_UsbDeviceManager(JavaVM *vm, JNIEnv *env) {
325 gvm = vm;
326
327 jclass clazz = env->FindClass("com/android/server/usb/UsbDeviceManager");
328 if (clazz == NULL) {
329 ALOGE("Can't find com/android/server/usb/UsbDeviceManager");
330 return -1;
331 }
332
333 gUpdateGadgetStateMethod =
334 GetMethodIDOrDie(env, clazz, "updateGadgetState", "(Ljava/lang/String;)V");
335
336 clazz = env->FindClass("android/os/ParcelFileDescriptor");
337 LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
338 gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
339 gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
340 LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
341 "Unable to find constructor for android.os.ParcelFileDescriptor");
342
343 return jniRegisterNativeMethods(env, "com/android/server/usb/UsbDeviceManager",
344 method_table, NELEM(method_table));
345 }
346 };
347