1 /*
2 * Copyright (C) 2018 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 "UsbAlsaJackDetectorJNI"
18 #include "utils/Log.h"
19
20 #include "jni.h"
21 #include <nativehelper/JNIHelp.h>
22 #include "android-base/strings.h"
23 #include "android_runtime/AndroidRuntime.h"
24 #include "android_runtime/Log.h"
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include <tinyalsa/asoundlib.h>
33
34 #define DRIVER_NAME "/dev/usb_accessory"
35
36 #define USB_IN_JACK_SUFFIX "Input Jack"
37 #define USB_OUT_JACK_SUFFIX "Output Jack"
38
39 namespace android
40 {
41
find_mixer_with_suffix(struct mixer * card_mixer,const char * suffix)42 static struct mixer_ctl* find_mixer_with_suffix(struct mixer* card_mixer, const char* suffix) {
43 int id = 0;
44 struct mixer_ctl* ctl;
45 while ((ctl = mixer_get_ctl(card_mixer, id++)) != NULL) {
46 const char* name = mixer_ctl_get_name(ctl);
47 if (android::base::EndsWith(name, suffix)) {
48 return ctl;
49 }
50 }
51 return NULL;
52 }
53
is_jack_connected(jint card,const char * suffix)54 static jboolean is_jack_connected(jint card, const char* suffix) {
55 struct mixer* card_mixer = mixer_open(card);
56 if (card_mixer == NULL) {
57 return true;
58 }
59 struct mixer_ctl* ctl = find_mixer_with_suffix(card_mixer, suffix);
60 if (!ctl) {
61 return true;
62 }
63 mixer_ctl_update(ctl);
64 int val = mixer_ctl_get_value(ctl, 0);
65 ALOGI("%s - value %d\n", mixer_ctl_get_name(ctl), val);
66 mixer_close(card_mixer);
67
68 return val != 0;
69 }
70
android_server_UsbAlsaJackDetector_hasJackDetect(JNIEnv *,jobject,jint card)71 static jboolean android_server_UsbAlsaJackDetector_hasJackDetect(JNIEnv* /* env */,
72 jobject /* thiz */,
73 jint card)
74 {
75 struct mixer* card_mixer = mixer_open(card);
76 if (card_mixer == NULL) {
77 return false;
78 }
79
80 jboolean has_jack = false;
81 if ((find_mixer_with_suffix(card_mixer, USB_IN_JACK_SUFFIX) != NULL) ||
82 (find_mixer_with_suffix(card_mixer, USB_OUT_JACK_SUFFIX) != NULL)) {
83 has_jack = true;
84 }
85 mixer_close(card_mixer);
86 return has_jack;
87 }
88
android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv *,jobject,jint card)89 static jboolean android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv* /* env */,
90 jobject /* thiz */,
91 jint card)
92 {
93 return is_jack_connected(card, USB_IN_JACK_SUFFIX);
94 }
95
android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv *,jobject,jint card)96 static jboolean android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv* /* env */,
97 jobject /* thiz */,
98 jint card)
99 {
100 return is_jack_connected(card, USB_OUT_JACK_SUFFIX);
101 }
102
android_server_UsbAlsaJackDetector_jackDetect(JNIEnv * env,jobject thiz,jint card)103 static void android_server_UsbAlsaJackDetector_jackDetect(JNIEnv* env,
104 jobject thiz,
105 jint card) {
106 jclass jdclass = env->GetObjectClass(thiz);
107 jmethodID method_jackDetectCallback = env->GetMethodID(jdclass, "jackDetectCallback", "()Z");
108 if (method_jackDetectCallback == NULL) {
109 ALOGE("Can't find jackDetectCallback");
110 return;
111 }
112
113 struct mixer* m = mixer_open(card);
114 if (!m) {
115 ALOGE("Jack detect unable to open mixer\n");
116 return;
117 }
118 mixer_subscribe_events(m, 1);
119 do {
120
121 // Wait for a mixer event. Retry if interrupted, exit on error.
122 int retval;
123 do {
124 retval = mixer_wait_event(m, -1);
125 } while (retval == -EINTR);
126 if (retval < 0) {
127 break;
128 }
129 mixer_consume_event(m);
130 } while (env->CallBooleanMethod(thiz, method_jackDetectCallback));
131
132 mixer_close(m);
133 return;
134 }
135
136 static const JNINativeMethod method_table[] = {
137 { "nativeHasJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_hasJackDetect },
138 { "nativeInputJackConnected", "(I)Z",
139 (void*)android_server_UsbAlsaJackDetector_inputJackConnected },
140 { "nativeOutputJackConnected", "(I)Z",
141 (void*)android_server_UsbAlsaJackDetector_outputJackConnected },
142 { "nativeJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_jackDetect },
143 };
144
register_android_server_UsbAlsaJackDetector(JNIEnv * env)145 int register_android_server_UsbAlsaJackDetector(JNIEnv *env)
146 {
147 jclass clazz = env->FindClass("com/android/server/usb/UsbAlsaJackDetector");
148 if (clazz == NULL) {
149 ALOGE("Can't find com/android/server/usb/UsbAlsaJackDetector");
150 return -1;
151 }
152
153 return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
154 method_table, NELEM(method_table));
155 }
156
157 }
158