/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.arakhne.afc.vmutil; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.vmutil.locale.Locale; /** * This class stores several information given by * the Android operating systems. * The stored informations are used by the arakhneVmutil * tools to proceed several tasks, such as {@link OperatingSystem}. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 7.0 */ public final class Android { /** Name of the home directory. */ public static final String HOME_DIRECTORY = "sdcard"; //$NON-NLS-1$ /** Name of the system-wide configuration directory. */ public static final String CONFIGURATION_DIRECTORY = "config"; //$NON-NLS-1$ /** Name of the system-wide data directory. */ public static final String DATA_DIRECTORY = "data"; //$NON-NLS-1$ private static SoftReference<Object> context; private static WeakReference<Object> contextResolver; private static WeakReference<ClassLoader> contextClassLoader; private Android() { // } /** Make a valid android application name from the given application name. * A valid android application name is a package name followed by the name * of the application. * * @param applicationName is the simple application name. * @return the android application name. */ @Pure public static String makeAndroidApplicationName(String applicationName) { final String fullName; if (applicationName.indexOf('.') >= 0) { fullName = applicationName; } else { fullName = "org.arakhne.partnership." + applicationName; //$NON-NLS-1$ } return fullName; } /** Replies the class {@code Context} from Android. * * @return the class {@code Context} from Android. * @throws AndroidException when the class cannot be found. */ @Pure public static Class<?> getContextClass() throws AndroidException { try { final ClassLoader loader = ClassLoaderFinder.findClassLoader(); return Class.forName("android.content.Context", true, loader); //$NON-NLS-1$ } catch (Throwable e) { throw new AndroidException(e); } } /** Replies the class {@code ContextResolver} from Android. * * @return the class {@code ContextResolver} from Android. * @throws AndroidException when the class cannot be found. */ @Pure public static Class<?> getContextResolverClass() throws AndroidException { try { final ClassLoader loader = ClassLoaderFinder.findClassLoader(); return Class.forName("android.content.ContentResolver", true, loader); //$NON-NLS-1$ } catch (Throwable e) { throw new AndroidException(e); } } private static Class<?> getInnerClass(String enclosingClassname, String innerClassname) throws AndroidException { final ClassLoader loader = ClassLoaderFinder.findClassLoader(); final Throwable ex; try { return Class.forName( enclosingClassname + "$" + innerClassname, //$NON-NLS-1$ true, loader); } catch (Throwable e) { ex = e; } try { final Class<?> enclosingClass = Class.forName(enclosingClassname, true, loader); for (final Class<?> innerClass : enclosingClass.getClasses()) { if (innerClassname.equals(innerClass.getName())) { return innerClass; } } } catch (Throwable exception) { // } throw new AndroidException(ex); } /** Replies the class {@code Secure} from Android. * * @return the class {@code Secure} from Android. * @throws AndroidException when the class cannot be found. */ @Pure public static Class<?> getSecureSettingsClass() throws AndroidException { return getInnerClass( "android.provider.Settings", //$NON-NLS-1$ "Secure"); //$NON-NLS-1$ } /** Replies the class {@code Secure} from Android. * * @return the class {@code Secure} from Android. * @throws AndroidException when the class cannot be found. */ @Pure public static Class<?> getSystemSettingsClass() throws AndroidException { return getInnerClass( "android.provider.Settings", //$NON-NLS-1$ "System"); //$NON-NLS-1$ } /** Extract informations from the current {@code Context} of the android task. * * @param androidContext is the Android {@code Context}. * @throws AndroidException when the information cannot be extracted. */ public static void initialize(Object androidContext) throws AndroidException { assert androidContext != null; try { final Class<?> contextType = androidContext.getClass(); final Class<?> contextClass = getContextClass(); if (!contextClass.isAssignableFrom(contextType)) { throw new AndroidException(Locale.getString("E1", androidContext)); //$NON-NLS-1$ } synchronized (Android.class) { contextResolver = null; context = new SoftReference<>(androidContext); } } catch (AssertionError e) { throw e; } catch (Throwable e) { throw new AndroidException(e); } ClassLoaderFinder.setPreferredClassLoader(getContextClassLoader()); } /** Replies the current {@code Context} for the android task. * * @return the current {@code Context} for the android task. * @throws AndroidException when the context is <code>null</code>. * @see #initialize(Object) */ @Pure public static Object getContext() throws AndroidException { final Object ctx; synchronized (Android.class) { if (context == null) { throw new AndroidException(); } ctx = context.get(); } if (ctx == null) { throw new AndroidException(); } return ctx; } /** Replies the class loader of the current Android context. * * @return class loader used by the current Android context. * @throws AndroidException when the context is <code>null</code>. * @see #initialize(Object) */ @Pure public static ClassLoader getContextClassLoader() throws AndroidException { synchronized (Android.class) { final ClassLoader cl = (contextClassLoader == null) ? null : contextClassLoader.get(); if (cl != null) { return cl; } } final Object context = getContext(); try { final Method method = context.getClass().getMethod("getClassLoader"); //$NON-NLS-1$ final Object classLoader = method.invoke(context); final ClassLoader cl = (ClassLoader) classLoader; synchronized (Android.class) { contextClassLoader = new WeakReference<>(cl); } return cl; } catch (Exception e) { throw new AndroidException(e); } } /** Replies the current {@code ContextResolver} for the android task. * * @return the current {@code ContextResolver} for the android task. * @throws AndroidException when the context is <code>null</code>. * @see #initialize */ @Pure public static Object getContextResolver() throws AndroidException { Object resolver; synchronized (Android.class) { resolver = (contextResolver == null) ? null : contextResolver.get(); } if (resolver == null) { final Object context = getContext(); try { final Class<?> resolverType = getContextResolverClass(); final Class<?> contextType = context.getClass(); final Method getContextResolverMethod = contextType.getMethod("getContentResolver"); //$NON-NLS-1$ resolver = getContextResolverMethod.invoke(context); resolver = resolverType.cast(resolver); synchronized (Android.class) { contextResolver = new WeakReference<>(resolver); } } catch (AssertionError e) { throw e; } catch (Throwable e) { throw new AndroidException(e); } } return resolver; } /** * This exception is thrown when the {@link Android} attributes * are not correctly initialized. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 7.0 */ public static class AndroidException extends Exception { private static final long serialVersionUID = 1521675695582278476L; /** Construct the exception. */ public AndroidException() { // } /** Construct the exception. * * @param message is the error message. */ public AndroidException(String message) { super(message); } /** Construct the exception. * @param exception is the cause of this exception. */ public AndroidException(Throwable exception) { super(exception); } /** Construct the exception. * @param message is the error message. * @param exception is the cause of this exception. */ public AndroidException(String message, Throwable exception) { super(message, exception); } } }