1 /*
2  **
3  ** Copyright 2008, The Android Open Source Project
4  **
5  ** Licensed under the Apache License, Version 2.0 (the "License");
6  ** you may not use this file except in compliance with the License.
7  ** You may obtain a copy of the License at
8  **
9  **     http://www.apache.org/licenses/LICENSE-2.0
10  **
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  */
17 
18 #define LOG_TAG "ToneGenerator"
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 
23 #include <jni.h>
24 #include <nativehelper/JNIHelp.h>
25 #include <nativehelper/ScopedUtfChars.h>
26 #include "core_jni_helpers.h"
27 
28 #include <utils/Log.h>
29 #include <media/AudioSystem.h>
30 #include <media/ToneGenerator.h>
31 
32 // ----------------------------------------------------------------------------
33 
34 using namespace android;
35 
36 struct fields_t {
37     jfieldID context;
38 };
39 static fields_t fields;
40 
getNativeToneGenerator(JNIEnv * env,jobject thiz)41 static sp<ToneGenerator> getNativeToneGenerator(JNIEnv *env, jobject thiz) {
42     auto toneGen = sp<ToneGenerator>::fromExisting(
43             reinterpret_cast<ToneGenerator *>(env->GetLongField(thiz, fields.context)));
44     if (toneGen == nullptr) {
45         jniThrowRuntimeException(env, "Method called after release()");
46     }
47     ALOGV("ToneGenerator address %p", toneGen.get());
48     return toneGen;
49 }
50 
setNativeToneGenerator(JNIEnv * env,jobject thiz,const sp<ToneGenerator> & toneGen)51 static sp<ToneGenerator> setNativeToneGenerator(JNIEnv *env, jobject thiz,
52                                                 const sp<ToneGenerator> &toneGen) {
53     auto oldToneGen = sp<ToneGenerator>::fromExisting(
54             reinterpret_cast<ToneGenerator *>(env->GetLongField(thiz, fields.context)));
55     ALOGV("ToneGenerator address changed from %p to %p", oldToneGen.get(), toneGen.get());
56     auto id = reinterpret_cast<void *>(setNativeToneGenerator);
57     if (toneGen != nullptr) {
58         toneGen->incStrong(id);
59     }
60     if (oldToneGen != nullptr) {
61         oldToneGen->decStrong(id);
62     }
63     env->SetLongField(thiz, fields.context, (jlong)toneGen.get());
64     return oldToneGen;
65 }
66 
android_media_ToneGenerator_startTone(JNIEnv * env,jobject thiz,jint toneType,jint durationMs)67 static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType,
68                                                       jint durationMs) {
69     ALOGV("%s jobject: %p", __func__, thiz);
70     auto lpToneGen = getNativeToneGenerator(env, thiz);
71     return (lpToneGen != nullptr)
72             ? lpToneGen->startTone((ToneGenerator::tone_type)toneType, durationMs)
73             : false;
74 }
75 
android_media_ToneGenerator_stopTone(JNIEnv * env,jobject thiz)76 static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
77     ALOGV("%s jobject: %p", __func__, thiz);
78     auto lpToneGen = getNativeToneGenerator(env, thiz);
79     if (lpToneGen != nullptr) lpToneGen->stopTone();
80 }
81 
android_media_ToneGenerator_getAudioSessionId(JNIEnv * env,jobject thiz)82 static jint android_media_ToneGenerator_getAudioSessionId(JNIEnv *env, jobject thiz) {
83     ALOGV("%s jobject: %p", __func__, thiz);
84     auto lpToneGen = getNativeToneGenerator(env, thiz);
85     return (lpToneGen != nullptr) ? lpToneGen->getSessionId() : 0;
86 }
87 
android_media_ToneGenerator_release(JNIEnv * env,jobject thiz)88 static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
89     ALOGV("%s jobject: %p", __func__, thiz);
90     setNativeToneGenerator(env, thiz, nullptr);
91 }
92 
android_media_ToneGenerator_native_setup(JNIEnv * env,jobject thiz,jint streamType,jint volume,jstring opPackageName)93 static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, jint streamType,
94                                                      jint volume, jstring opPackageName) {
95     ALOGV("%s jobject: %p", __func__, thiz);
96     ScopedUtfChars opPackageNameStr{env, opPackageName};
97     sp<ToneGenerator> lpToneGen = sp<ToneGenerator>::make((audio_stream_type_t)streamType,
98                                     AudioSystem::linearToLog(volume),
99                                     true /*threadCanCallJava*/,
100                                     opPackageNameStr.c_str());
101     if (!lpToneGen->isInited()) {
102         ALOGE("ToneGenerator init failed");
103         jniThrowRuntimeException(env, "Init failed");
104         return;
105     }
106     // Stow our new C++ ToneGenerator in an opaque field in the Java object.
107     setNativeToneGenerator(env, thiz, lpToneGen);
108 }
109 
android_media_ToneGenerator_native_finalize(JNIEnv * env,jobject thiz)110 static void android_media_ToneGenerator_native_finalize(JNIEnv *env, jobject thiz) {
111     ALOGV("%s jobject: %p", __func__, thiz);
112     android_media_ToneGenerator_release(env, thiz);
113 }
114 
115 // ----------------------------------------------------------------------------
116 
117 static const JNINativeMethod gMethods[] =
118         {{"startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone},
119          {"stopTone", "()V", (void *)android_media_ToneGenerator_stopTone},
120          {"getAudioSessionId", "()I", (void *)android_media_ToneGenerator_getAudioSessionId},
121          {"release", "()V", (void *)android_media_ToneGenerator_release},
122          {"native_setup", "(IILjava/lang/String;)V",
123           (void *)android_media_ToneGenerator_native_setup},
124          {"native_finalize", "()V", (void *)android_media_ToneGenerator_native_finalize}};
125 
register_android_media_ToneGenerator(JNIEnv * env)126 int register_android_media_ToneGenerator(JNIEnv *env) {
127     jclass clazz = FindClassOrDie(env, "android/media/ToneGenerator");
128 
129     fields.context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
130     ALOGV("register_android_media_ToneGenerator ToneGenerator fields.context: %p", fields.context);
131 
132     return RegisterMethodsOrDie(env, "android/media/ToneGenerator", gMethods, NELEM(gMethods));
133 }
134