diff --git a/shell/android/src/com/reicast/emulator/periph/MOGAInput.java b/shell/android/src/com/reicast/emulator/periph/MOGAInput.java index a587512ba..78f506488 100644 --- a/shell/android/src/com/reicast/emulator/periph/MOGAInput.java +++ b/shell/android/src/com/reicast/emulator/periph/MOGAInput.java @@ -90,7 +90,8 @@ public final class MOGAInput .getDefaultSharedPreferences(act.getApplicationContext()); mController = Controller.getInstance(act); - mController.init(); +// mController.init(); + MogaHack.init(mController, this.act); mListener = new ExampleControllerListener(); mController.setListener(mListener, new Handler()); } diff --git a/shell/android/src/com/reicast/emulator/periph/MogaHack.java b/shell/android/src/com/reicast/emulator/periph/MogaHack.java new file mode 100644 index 000000000..9e28f7331 --- /dev/null +++ b/shell/android/src/com/reicast/emulator/periph/MogaHack.java @@ -0,0 +1,118 @@ +/** + * Mupen64PlusAE, an N64 emulator for the Android platform + * + * Copyright (C) 2013 Paul Lamb + * + * This file is part of Mupen64PlusAE. + * + * Mupen64PlusAE is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * Mupen64PlusAE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mupen64PlusAE. If + * not, see . + * + * Authors: Paul Lamb + */ +package com.reicast.emulator.periph; + +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.util.Log; + +import com.bda.controller.Controller; +import com.bda.controller.IControllerService; + +/** + * Temporary hack for crash in MOGA library on Lollipop. This hack can be removed once MOGA fixes + * their library. The actual issue is caused by the use of implicit service intents, which are + * illegal in Lollipop, as seen in the logcat message below. + * + *
+ * {@code Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService } }
+ * 
+ * + * @see MOGA developer site + * @see + * Discussion on explicit intents + */ +public class MogaHack +{ + public static void init( Controller controller, Context context ) + { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) + { + boolean mIsBound = false; + java.lang.reflect.Field fIsBound = null; + android.content.ServiceConnection mServiceConnection = null; + java.lang.reflect.Field fServiceConnection = null; + try + { + Class cMogaController = controller.getClass(); + fIsBound = cMogaController.getDeclaredField( "mIsBound" ); + fIsBound.setAccessible( true ); + mIsBound = fIsBound.getBoolean( controller ); + fServiceConnection = cMogaController.getDeclaredField( "mServiceConnection" ); + fServiceConnection.setAccessible( true ); + mServiceConnection = ( android.content.ServiceConnection ) fServiceConnection.get( controller ); + } + catch( NoSuchFieldException e ) + { + Log.e( "MogaHack", "MOGA Lollipop Hack NoSuchFieldException (get)", e ); + } + catch( IllegalAccessException e ) + { + Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (get)", e ); + } + catch( IllegalArgumentException e ) + { + Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (get)", e ); + } + if( ( !mIsBound ) && ( mServiceConnection != null ) ) + { + // Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757 + Intent intent = new Intent( IControllerService.class.getName() ); + List resolveInfos = context.getPackageManager().queryIntentServices( intent, 0 ); + if( resolveInfos == null || resolveInfos.size() != 1 ) + { + Log.e( "MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security." ); + return; + } + ServiceInfo serviceInfo = resolveInfos.get( 0 ).serviceInfo; + String packageName = serviceInfo.packageName; + String className = serviceInfo.name; + intent.setComponent( new ComponentName( packageName, className ) ); + + // Start the service explicitly + context.startService( intent ); + context.bindService( intent, mServiceConnection, 1 ); + try + { + fIsBound.setBoolean( controller, true ); + } + catch( IllegalAccessException e ) + { + Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (set)", e ); + } + catch( IllegalArgumentException e ) + { + Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (set)", e ); + } + } + } + else + { + controller.init(); + } + } +}