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.