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