Since what API level does Scanner implement Closeable? - android

I'm running into an issue similar to this one, except with java.util.Scanner. I have this static method:
public static void close(final Closeable c) {
if(c != null) {
Log.debug(TAG, "instance of " + c.getClass().getName());
try {
c.close();
} catch (IOException e) {
// ignore
}
}
}
When passed a Scanner on API 15, it crashes like so:
08-29 20:33:42.979: E/AndroidRuntime(2245): FATAL EXCEPTION: main
08-29 20:33:42.979: E/AndroidRuntime(2245): java.lang.IncompatibleClassChangeError: interface not implemented
08-29 20:33:42.979: E/AndroidRuntime(2245): at com.mycompany.myapp.IOUtil.close(IOUtil.java:36)
[more lines omitted]
The docs say that Scanner implements Closeable even if you set the doc API level below 15. Could this be a vendor-specific issue? I only have one API 15 device to test on, and nothing between that and API 19, which works fine.

Scanner implements Closeable since KitKat
You can check here
4.4 kitkat
https://android.googlesource.com/platform/libcore/+/kitkat-release/luni/src/main/java/java/util/Scanner.java
4.3_r3 https://android.googlesource.com/platform/libcore/+/android-4.3_r3/luni/src/main/java/java/util/Scanner.java

Not a direct answer to my question, but in light of Derek Fung's answer, my close() method now looks like this:
public static void close(final Closeable c) {
if(c != null) {
/* Several classes that were made Closeable in Java 1.7 also
* became Closeable in Android API 19, including java.net.Socket
* and java.util.Scanner. The fact that the minimum SDK version
* is lower than that does not cause a compiler error when objects
* of those types are passed to this method, resulting in a
* IncompatibleClassChangeError at runtime. Hence this seemingly
* pointless test. */
if(c instanceof Closeable) {
try {
c.close();
} catch (IOException e) {
// ignore
}
}
else {
Log.debug(TAG, c.getClass().getName() + " does not implement Closeable; attempting reflection");
try {
final Method m = c.getClass().getMethod("close");
m.invoke(c);
}
catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException e) {
// shouldn't happen
Log.warn(TAG, "could not close " + c.getClass().getName(), e);
} catch (InvocationTargetException e) {
// ignore
}
}
}
}
The reflection code is executed and seems to work fine on API 15.

Related

How to determine/get correct S Voice packet in Android

I am using Android to turn on my S Voice application in Android. As previous work, I will use the follows code to turn on it
String SVOICE_PACKAGE_NAME = "com.vlingo.midas";
String SVOICE_LISTEN_ACTION = "com.sec.action.SVOICE";
Intent intent = new Intent();
intent.setPackage(SVOICE_PACKAGE_NAME);
intent.setAction(SVOICE_LISTEN_ACTION);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
getApplication().startActivity(intent);
} catch (final ActivityNotFoundException e) {
e.printStackTrace();
} catch (final Exception e) {
e.printStackTrace();
}
The above code worked well in Galaxy S4 with Android 5.0. However, the issue comes from first and second lines in Galaxy S7 with Android 6.0. In Galaxy S7 with Android 6.0, the first and second lines have to modify as
SVOICE_PACKAGE_NAME = "com.samsung.voiceserviceplatform";
SVOICE_LISTEN_ACTION = "com.sec.action.SVOICE";
And also the application name S Voice with changing from "S Voice" to "S Voice App". That changing gives me a difficult work. Hence, I want to determine the S Voice App in my phone before deciding calls these function. Currently, I do not know the changing is from Android version or the device. Could you have any idea to adapt the issue in various phones: S4 and S7?
Whenever opening applications, there could be package or application name differences. Here is a standard utility method to check:
/**
* Check if the user has a package installed
*
* #param ctx the application context
* #param packageName the application package name
* #return true if the package is installed
*/
public static boolean isPackageInstalled(#NonNull final Context ctx, #NonNull final String packageName) {
if (DEBUG) {
MyLog.i(CLS_NAME, "isPackageInstalled");
}
try {
ctx.getApplicationContext().getPackageManager().getApplicationInfo(packageName, 0);
return true;
} catch (final PackageManager.NameNotFoundException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: NameNotFoundException");
}
} catch (final NullPointerException e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: NullPointerException");
}
} catch (final Exception e) {
if (DEBUG) {
MyLog.w(CLS_NAME, "isPackageInstalled: Exception");
}
}
return false;
}
You'll need to remove my custom logging.

ContentResolver.isSyncActive on Android 5.0 throws IllegalArgumentException

I have been using isSyncActive() to check is sync active. Everything working well on Android versions < 5.0 but on new android version 5.0 it throws exception java.lang.IllegalArgumentException: account must not be null. Is there any solution and why is this happening only in the above mentioned android version.
The code for the content resolver is open source: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/content/ContentResolver.java if you are interested. I'm also having trouble with other things associated with the 5.0 update, which is why I happened across this post. Looks like you need to pass a valid account object to the method to get it to work.
public static boolean isSyncActive(Account account, String authority) {
if (account == null) {
throw new IllegalArgumentException("account must not be null");
}
if (authority == null) {
throw new IllegalArgumentException("authority must not be null");
}
try {
return getContentService().isSyncActive(account, authority, null);
} catch (RemoteException e) {
throw new RuntimeException("the ContentService should always be reachable", e);
}
}

DexClassLoader, reload Code fails with Signal 7

I'm trying to build a plugin-System, where DexClassLoader is fetching code from other installed apks containing fragments(my plugins), and showing them in my host. This is working quite nice.
I also like to make the plugins hotswappable, this means I can change the code from a plugin, install it new and the host will notice and will load the new code. This also works, if I'm changing the code for the first time. (Although I thought it shouldn't, it seems I've got a wrong understanding of this code:
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
)
If i'm trying it a second time with the same plugin, the host shuts down at requiredClass = classLoader.loadClass(fullName); with something like
libc Fatal signal 7 (SIGBUS) at 0x596ed4d6 (code=2), thread 28814
(ctivityapp.host)
Does anybody has a deeper insight in the functionality of DexClassLoader and may tell me, what is happening here? I'm quite stuck at this.
Heres the full code of the method loading the foreign code:
/**
* takes the name of a package as String, and tries to load the code from the corresponding akp using DexclassLaoder.
* Checking if a package is a valid plugin must be done before calling this.
* The Plugin must contain a public class UI that extends Fragment and implements plugin as a starting point for loading
* #param packageName The full name of the package, as String
* #return the plugins object if loaded, null otherwise
*/
private Plugin attachPluginToHost(String packageName) {
try {
Class<?> requiredClass = null;
final ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName,0);
final String apkPath = info.sourceDir;
final File dexTemp = context.getDir("temp_folder", 0);
final String fullName = packageName + ".UI";
boolean isLoaded = true;
// Check if class loaded
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
if (!isLoaded) {
final DexClassLoader classLoader = new DexClassLoader(apkPath, dexTemp.getAbsolutePath(), null, context.getApplicationContext().getClassLoader());
requiredClass = classLoader.loadClass(fullName);
}
if (null != requiredClass) {
// Try to cast to required interface to ensure that it's can be cast
final Plugin plugin = Plugin.class.cast(requiredClass.newInstance());
installedPlugins.put(plugin.getName(), plugin);
return plugin;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Many thanks in advance!
Not that it really matters (As nobody is actually viewing this), or that I even understand what's going on, but deleting the corresponding file of the plugin in dexTemp.getAbsolutePath() before reloading it solves the problem.
PS: Tumbleweed-Badge, YAY!

Android Library target compilation and objects in later Android versions

I am building an Android Library and have a method getting some information about the device. Our target is to support 2.2 and up but was wondering if there is a way to collect information introduced in later versions (ex device serial in 2.3) and have the application set with version 2.2 to compile.
After searching around I found people using code like:
private static String getHardwareSerial() {
try {
return Build.SERIAL;
} catch (VerifyError e) {
//Android 8 and previous did not have this information
return Build.UNKNOWN;
}
}
However, with this code present, my sample application using our library fails to build when setting the build target to 8. Any suggestions or do we have to live with our clients setting their target to 9 to get this info?
You could do it through reflection:
public static String getHardwareSerial() {
try {
Field serialField = Build.class.getDeclaredField("SERIAL");
return (String)serialField.get(null);
}
catch (NoSuchFieldException nsf) {
}
catch (IllegalAccessException ia) {
}
return Build.UNKNOWN;
}
If the field isn't found (on earlier versions of the OS) it'll throw an exception that will be ignored and then fall through to return Build.UNKNOWN.

TTS callback: dispatch completed to 1

I created a small TTS app implementing OnUtteranceCompleteListener and, while things seem to be working exactly as expected, I noticed the following on LogCat (one for each completed utterance):
03-01 20:47:06.436:
VERBOSE/TtsService(381): TTS callback:
dispatch completed to 1
Again, this seems to be benign but I don't understand what '1' means. All such lines for all utterances say "completed to 1", even for utterance IDs that are greater than 1.
What does '1' mean in this log?
BTW, this message is not generated by my code but rather by the TTS engine (Pico) itself.
Looking at the TTSService.java source code available at http://eyes-free.googlecode.com you can find the function dispatchUtteranceCompletedCallback():
private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
/* Legacy support for TTS */
final int oldN = mCallbacksOld.beginBroadcast();
for (int i = 0; i < oldN; i++) {
try {
mCallbacksOld.getBroadcastItem(i).markReached("");
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
try {
mCallbacksOld.finishBroadcast();
} catch (IllegalStateException e) {
// May get an illegal state exception here if there is only
// one app running and it is trying to quit on completion.
// This is the exact scenario triggered by MakeBagel
return;
}
/* End of legacy support for TTS */
ITtsCallbackBeta cb = mCallbacksMap.get(packageName);
if (cb == null) {
return;
}
Log.v(SERVICE_TAG, "TTS callback: dispatch started");
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
try {
cb.utteranceCompleted(utteranceId);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
mCallbacks.finishBroadcast();
Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
}
1 is the current value of N, which is initialized by the return value from mCallbacks.beginBroadcast().
beginBroadcast() is a method of the class RemoteCallbackList and its documentation states that it:
Returns the number of callbacks in the
broadcast, to be used with
getBroadcastItem(int) to determine the
range of indices you can supply
Does this help?

Categories

Resources