Hi to all android devlopers.
Please, help me out from this:
1)How do i use non public classes in android like telephony, android.telephony.CallManager in my android application?
2) How do I import this telephony packages in my activity class and make it allow to access its functionality?
You can't and you shouldn't. These internal classes/APIs can change at any time without warning during an Android upgrade. there's no guarantee that they're implemented in the exact same way across different vendors.
Such changes can cause your application to break.
You should only use the public classes included in the android.jar
Reflection?
ClassLoader classLoader = TestActivity.class.getClassLoader();
final ClassLoader classLoader = this.getClass().getClassLoader();
try {
final Class<?> classCallManager =
classLoader.loadClass("com.android.internal.telephony.CallManager");
} catch (final ClassNotFoundException e) {
Log.e("TestActivity", e);
}
And add
READ_PHONE_STATE
to your Manifest.xml
Related
We have a shared library that contains version info and is referenced by all our projects in our Visual Studio Solution.
For the most part, we can reference the version string from every project and the dll reflect the info accordingly.
My issue here is, with our Android application (xamarin based). It has a manifest file which contains the versionName and versionCode.
How can we make those values in our android manifest file read from our shared project?
My understanding is that, it is not possible. Because
The manifest file presents essential information about your app to the Android system, information the system must have before it can run any of the app's code.
From Google's documentation
So this is a file that is required before the App builds.
C# Code in Shared Project (SAP/PCL) is ready to be used only after successful Compilation. So logically setting the Version Code and Version Name in Android Manifest File from Shared logic is not possible.
Another standard approach would be to set it from String Resource (XML) file in Android. You may have to copy and paste the value from Shared Project to strings.xml file and refer it in manifest, like
#string/versionCode
Note: I do not know anything about xamarin.
In java you can get the versioninfo from the manifest like this
public static String getAppVersionName(final Context context) {
try {
final String versionName = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0).versionName;
return versionName;
} catch (final NameNotFoundException e) {
}
return null;
}
I assume that xamarin has some mechanism to call PackageManager to get Packageinfo, too
You could do this by using a Dependency Service. Here's a great article on them: https://developer.xamarin.com/guides/xamarin-forms/dependency-service/
The idea would be your Dependency Service would expose the Android specific information to the shared code library.
For instance you might have an interface in your common code declared such as:
public interface IPlatformVersionInfo
{
string GetOSVersion ();
}
Now, in your Android library you would implement it:
public class PlatformVersionInfo : IPlatformVersionInfo
{
public string GetOSVersion () {
return Android.OS.Build.VERSION.SdkInt.ToString ();
}
}
Finally, in your common code you would use your dependency service of choice to invoke an instance of it:
var osVersion = DependencyService.Get<IPlatformVersionInfo>().GetOSVersion ();
Of course this is somewhat pseudo-code and depending what dependency service you choose the code may look a bit different.
I am new in Android programming. I want to invoke a method in the class BatteryService by reflection. However, the following code fails above Android 5.0, including the newest Android 6.0.1, though it succeeds in Android 4.3. I have googled for days. But I cannot find any useful answers.
try{
Class myclass = Class.forName("com.android.server.BatteryService");
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.d("xx", "ClassNotFound!");
}
I have checked the source of Android 4.3, 5.0 and 6.0.1, and I am sure that the class BatteryService is in com.android.server. BTW, the modifier of BatteryService is public.
When running the above code, Android 5.0+ report an exception that the Class cannot be found. But the code works in Android 4.3. I wonder there are any new features introduced in Android 5.0 to preventing reflection?
Anyone knows the reason. Thanks a million!
As answered before, the classloader can't found the class. This is because the scope of the package is invisible if you don't have system's permissions (your apk should be installed in /system/app).
I recommend you to read this doc
You should avoid reflection and use the service class BatteryManager.
http://developer.android.com/reference/android/os/BatteryManager.html
Probably classloader doesn't know that class. App classloader and system classloader are not same.
EDITED
When starting systemserver, class path has set.
So, probably system app doesn't have this classloader.
https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/com/android/internal/os/ZygoteInit.java#461
/**
* Finish remaining work for the newly forked system server process.
*/
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
....
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
I have an app which uses Google Maps (v1) and from the crash reports, I am seeing this exception from time to time:
java.lang.NoClassDefFoundError: android.security.MessageDigest
at com.google.android.maps.KeyHelper.getSignatureFingerprint(KeyHelper.java:60)
at com.google.android.maps.MapActivity.createMap(MapActivity.java:513)
at com.google.android.maps.MapActivity.onCreate(MapActivity.java:409)
I have defined
<uses-library
android:name="com.google.android.maps"
android:required="true" />
inside the application tag and I am extending MapActivity as well. The application works fine on most devices but there are some uncommon ones that report this exception, usually on Android 4.0.4 like Woxter Tablet PC 90BL, TAB9008GBBK and other generic names.
From what I read in Stackoverflow, it is a problem in the ROM and it can be solved by the user doing some advanced tricks but what I want is to prevent this crash, as I don't think it can be solved, I just want to inform the user (and thell him to buy a better device :) and disable maps functionality instead of crashing. But I can't find a way to handle this error or test it with the devices I have.
Also my main activity is based on MapActivity so I don't know how can I handle this exception before opening it.
Disclaimer: I've not come across this error on any of my apps / devices but I solved a similar problem. May be that same technique can help you.
Given that the class is either unavailable or an exception occurrs while loading the class, why not try to force load it when your application starts ? Class.forName("android.security.MessageDigest") should load the class and you can catch the Error thrown from that call. I know its dirty, but it should work. You can declare a custom Application class on the manifest to make this check.
Class loading test
try
{
Class.forName("android.security.MessageDigest");
}
catch (Throwable e1)
{
e1.printStackTrace();
//Bad device
}
You can also perform a litmus test and check the functionality of the class should the class loading succeed by digesting a simple String.
Functional test
try
{
MessageDigest digester = MessageDigest.getInstance("MD5");
digester.update("test".getBytes("UTF-8"));
byte[] digest = digester.digest();
}
catch (Throwable e1)
{
e1.printStackTrace();
// Class available but not functional
}
If the class loading / litmus test fails, update a shared preference flag and let the user know that his device sucks :)
Try to change the import android.security.MessageDigest to java.security.MessageDigest
by the look at this link:
What is 'android.security.MessageDigest''?
It looks that the android.security.MessageDigest was remove from Honeycomb so change it to the java one. and check this link as well:
http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE
As been suggested there by #XGouchet:
Try downloading the latest version of the Google Maps API and rebuild your application with targetSDK set to the highest available (as of today it should be 17 / Jelly Bean).
The class android.security.MessageDigest is an abstract class (see MessageDigest API) what means that it can't be instantiated right away. So what happens is, that any time a device/app can't find an implementation of this class you will get the exception above, namely
java.lang.NoClassDefFoundError: android.security.MessageDigest
It's a good question why this happens. May be some phone vendors didn't ship their phone with the required library that actually implements this abstract class. I faced a similar issue with the TUN.ko module in the past.
Approach 1
What should help is, if you provide your own (empty) implementation of this class that "implements" the abstract classes and methods like this:
public class MessageDigestSpi extends Object {
byte[] engineDigest() { return new byte[0]; }
void engineReset() { }
void engineUpdate(byte[] input, int offset, int len) { }
}
public class MessageDigest extends MessageDigestSpi {
}
... and put those classes into the folder <src>/java/security/. So this way you provide your own implementation that is always found and might contain some code in order to inform the user or provide an alternative implementation.
So the remaining questions are: what does the app do, if the implementation is provided by the system, too and how to control that the system implementation is the first choice?
The answer: which implementation is chosen depends on the import order. Looking at Eclipse you can define the order in the project properties, Java build path, tab order and export. Be sure that you have any system libraries on top that might include the system implementation (most likely the Android libraries). This way the system searches in those libraries first. If nothing is found your classes get loaded and executed.
Approach 2
As an alternative to the implementation in an own abstract class you could of course simply instantiate the MessageDigest class, catch the NoClassDefFoundError exception and store the result for later evaluation:
import android.security.MessageDigest;
public class MessageDigestTester {
private static Boolean messageDigestAvailable = null;
public static Boolean isLibraryAvailable() {
if (messageDigestAvailable == null) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
messageDigestAvailable = true;
} catch (NoClassDefFoundError e) {
messageDigestAvailable = false;
}
}
return messageDigestAvailable;
}
}
Then use if (MessageDigestTester.isLibraryAvailable()) { } else { } in your code in order to encapsulate the usage of this library and to provide an alternative.
Approach two is easier to implement whereas approach one is the more sophisticated solution.
Hope this was helpful ... Cheers!
I downloaded the source code from the below link and added to my project.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/com/android/internal/os/PowerProfile.java
I am getting and it can not find R file shown below.
int id = com.android.internal.R.xml.power_profile;
Also can not import
import com.android.internal.util.XmlUtils;
I basically want to measure the power consumption of Android devices.
Personally using patached android.jar just causes headaches, using reflection is a 'simple' way of accessing PowerProfile.java. But as #FoamyGuy and countless others have noted this is hidden api so wrap it in a big try catch as it could break on later version of Android.
Class<?> powerProfileClazz = Class.forName("com.android.internal.os.PowerProfile");
//get constructor that takes a context object
Class[] argTypes = { Context.class };
Constructor constructor = powerProfileClazz
.getDeclaredConstructor(argTypes);
Object[] arguments = { context };
//Instantiate
Object powerProInstance = constructor.newInstance(arguments);
//define method
Method batteryCap = powerProfileClazz.getMethod("getBatteryCapacity", null);
//call method
Log.d(TAG, batteryCap.invoke(powerProInstance, null).toString());
Yes you can access the internal API that is com.android.internal.os.PowerProfile
just take a look at this link, and follow the step by step process.
You could use
int id = Resources.getSystem().getIdentifier("power_profile", "xml", "android");
But be aware of what FoamyGuy commented.
Easiest way to do this is to download the framework.jar of android and include that as external library in your project. After including the framework.jar in your android project you can find that resource file.
In Android I get the version of the SDK easily (Build.VERSION.SDK) but I need to use LabeledIntent only if the platform is newer than 1.6 (>Build.VERSION_CODES.DONUT)
I suppose that Reflection is necessary (I have read this link but it is not clear for a class or to me).
This is the code but it gives me an exception because in my Android 1.6, the compiler verifies if the package exists even if the condition is not applied:
Intent theIntent=....;
if(Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.DONUT)
{
try{
Intent intentChooser = Intent.createChooser(intent,"Choose between these programs");
Parcelable[] parcelable = new Parcelable[1];
parcelable[0] = new android.content.pm.LabeledIntent(theIntent, "", "Texto plano", 0);
intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, parcelable);
activity.startActivity(intentChooser);
}
catch(Exception e)
{
activity.startActivity(theIntent);
}
} else
{
activity.startActivity(intentMedicamento);
}
HOW I SOLVED IT, SOME NOTES TO THE RIGHT ANSWER
#Commonsware show me the way to do it. We create a bridge class so that depending on the API LEVEL, you instance one class that uses an API LEVEL or another class that uses another API LEVEL.
The only detail one beginner could forget is that you have to compile your app with the newest SDK you are goint to make reference.
public abstract class LabeledIntentBridge {
public abstract Intent BuildLabeledIntent(String URL, Intent theintent);
public static final LabeledIntentBridge INSTANCE=buildBridge();
private static LabeledIntentBridge buildBridge() {
int sdk=new Integer(Build.VERSION.SDK).intValue();
if (sdk<5) {
return(new LabeledIntentOld());
}
return(new LabeledIntentNew());
}
}
So in the LabeledIntentNew, I included all the code that refers to LabeledIntent only available in API LEVEL 5. In LabeledIntentOld, I can implement another kind of control, in my case I return the intent itself without doing nothing more.
The call to this class is done like this:
LabeledIntentBridge.INSTANCE.BuildLabeledIntent(URLtest,theIntent);
Follow the wrapper class pattern documented in the page you linked to above.
You have to use reflection...
The idea is good, but in your code you refer to LabeledIntent which is not available in 1.6. So when your app runs against 1.6 devices, it cannot find the class and crashes.
So the idea is to write code where you don't refer to LabeledIntent when running in 1.6. To do this, you can write a wrapper class (LabeledIntentWrapper) which extends LabeledIntent and call it in your function. So, in 1.6, the device will see a reference to a known class: LabeledIntentWrapper.