I'm working on a simple visualizer application that listens to audio output, produces an FFT(handled by visualizer API), does a little math, and wirelessly sends the info out for display. I'm having trouble getting the Visualizer API running on Android 8.0 on a Oneplus3, but it works fine on both simulated Android 6.0 & 8.0 phones.
Here's how I request the permissions I believe I need, first in the manifest:
<manifest>
<application>
...rest of application...
</application>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
</manifest>
Next I request some permissions at runtime:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextMessage = (TextView) findViewById(R.id.message);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
if (ContextCompat.checkSelfPermission(getApplicationContext(), permission.MODIFY_AUDIO_SETTINGS) == PackageManager.PERMISSION_DENIED)
Log.d("App", "No MODIFY_AUDIO_SETTINGS" );
else
Log.d("App", "Yes MODIFY_AUDIO_SETTINGS" );
if (ContextCompat.checkSelfPermission(getApplicationContext(), permission.RECORD_AUDIO) == PackageManager.PERMISSION_DENIED)
Log.d("App", "No RECORD_AUDIO" );
else
Log.d("App", "Yes RECORD_AUDIO" );
Log.d("App","Requesting permissions" );
ActivityCompat.requestPermissions( this, new String[]
{
permission.MODIFY_AUDIO_SETTINGS,
permission.RECORD_AUDIO
},1 );
Log.d("App","Requested perms");
}
I believe this should print the current permission state, then request all permissions I need. On a fresh install, it prompts for RECORD_AUDIO ( MODIFY_AUDIO_SETTINGS is not a dangerous permission, and appears to be automatically granted). Asynchronously, this is called:
public void onRequestPermissionsResult (int requestCode,
String[] permissions,
int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
boolean success = true;
for( int i = 0; i < permissions.length; ++i ) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED ) {
Log.d("App",permissions[i]+" granted");
} else {
Log.d("App",permissions[i]+" denied");
Toast.makeText(getApplicationContext(), "Needed " + permissions[i], Toast.LENGTH_SHORT).show();
success = false;
}
}
if( success ) {
Log.d("App","Setting up visualizer");
}
}
On Android 8.0, the log shows we have both permissions, and that if we request them anyways, they're granted. But when we get a little later in execution ( launching the visualizer is currently tied to a button press ),
D/App: Yes MODIFY_AUDIO_SETTINGS
D/App: Yes RECORD_AUDIO
D/App: Requesting permissions
D/App: Requested perms
D/AppTracker: App Event: start
D/OpenGLRenderer: HWUI GL Pipeline
D/AppTracker: App Event: stop
I/Adreno: QUALCOMM build : 08cdca0, I5f270ba9bc
Build Date : 09/18/17
OpenGL ES Shader Compiler Version: EV031.20.00.04
Local Branch :
Remote Branch : refs/tags/AU_LINUX_ANDROID_LA.UM.6.5.R1.08.00.00.312.025
Remote Branch : NONE
Reconstruct Branch : NOTHING
I/vndksupport: sphal namespace is not configured for this process. Loading /vendor/lib64/hw/gralloc.msm8996.so from the current namespace instead.
I/Adreno: PFP: 0x005ff087, ME: 0x005ff063
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 2
D/App: android.permission.MODIFY_AUDIO_SETTINGS granted
D/App: android.permission.RECORD_AUDIO granted
D/App: Setting up visualizer
D/AppTracker: App Event: start
I/vndksupport: sphal namespace is not configured for this process. Loading /vendor/lib64/hw/gralloc.msm8996.so from the current namespace instead.
E/AudioEffect: set(): AudioFlinger could not create effect, status: -1
E/visualizers-JNI: Visualizer initCheck failed -3
E/Visualizer-JAVA: Error code -3 when initializing Visualizer.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.rsaxvc.blequalizer, PID: 15981
java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -3
at android.media.audiofx.Visualizer.<init>(Visualizer.java:218)
at net.rsaxvc.blequalizer.MainActivity.setupVisualizerFxAndUI(MainActivity.java:133)
at net.rsaxvc.blequalizer.MainActivity.access$000(MainActivity.java:22)
at net.rsaxvc.blequalizer.MainActivity$1.onNavigationItemSelected(MainActivity.java:63)
at android.support.design.widget.BottomNavigationView$1.onMenuItemSelected(BottomNavigationView.java:182)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:171)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:973)
at android.support.design.internal.BottomNavigationMenuView$1.onClick(BottomNavigationMenuView.java:95)
at android.view.View.performClick(View.java:6289)
at android.view.View$PerformClick.run(View.java:24800)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6809)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
D/AppTracker: App Event: crash
Application terminated.
When calling
mVisualizer = new Visualizer(0);
We see: "E/AudioEffect: set(): AudioFlinger could not create effect, status: -1", from media/libmedia/AudioEffect.cpp, it looks like audioFlinger->createEffect failed and returned -1, equal to PERMISSION_DENIED, due to the following code in services/audioflinger/AudioFlinger.cpp:
// check audio settings permission for global effects
if (sessionId == AUDIO_SESSION_OUTPUT_MIX && !settingsAllowed()) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
and this in libs/audioflinger/AudioFlinger.cpp:
static bool settingsAllowed() {
#ifndef HAVE_ANDROID_OS
return true;
#endif
#if AUDIOFLINGER_SECURITY_ENABLED
if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
return ok;
#else
if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")))
LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest");
return true;
#endif
}
My guess is that native code isn't getting the same permission that the Java code sees. Or, that the native code is running as a different process than the application itself.
This appears to be different from Android Visualizer error only in Android 6.0 , where the app just wasn't asking for runtime permissions.
Part way through developing this app, my phone updated to Android 8.0. AFAICT, this broke something. Finally, uninstalling and reinstalling the app fixed it.