isInitialStickyBroadcast() is obviously only available after 2.0 (SDK 5).
I'm getting this error:
"Uncaught handler: thread main exiting due to uncaught exception
java.lang.VerifyError"
It's only happening on 1.6. Android 2.0 and up doesn't have any problems, but that's the main point of all.
I can't catch the Error/Exception (java.lang.VerifyError), and I know it's being caused by calling isInitialStickyBroadcast() which is not available in SDK 4, that's why it's wrapped in the SDK check.
I just need this BroadcastReceiver to work on 2.0+ and not break in 1.6, it's an app in the market, the UNDOCK feature is needed for users on 2.0+ but obviously not in 1.6 but there is a fairly substantial number of users still on 1.6.
Here's an easy-to-read version of part of the code I'm using. Notice that it's wrapped in an SDK check to only run on 2.0+, but the VerifyError is still showing up.
private BroadcastReceiver mUndockedReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//FROM ECLAIR FORWARD, BEFORE DONUT THIS INTENT WAS NOT IMPLEMENTED
if (Build.VERSION.SDK_INT >= 5)
{
if (!isInitialStickyBroadcast()) {
//Using constant instead of Intent.EXTRA_DOCK_STATE to avoid problems in older SDK versions
int dockState = intent.getExtras().getInt("android.intent.extra.DOCK_STATE", 1);
if (dockState == 0)
{
finish();
}
}
}
}
};
Your problem is that while you would not be executing isInitialStickyBroadcast(), the classloader attempts to resolve all methods when the class is loaded, so your SDK 4 devices fail at that point, since there is no isInitialStickyBroadcast().
You have two main options:
Use reflection.
Create two editions of your BroadcastReceiver, as public classes in their own files. One has the SDK 4 logic, one has the SDK 5+ logic. Register the one you want based on an SDK check at the time you call registerReceiver().
Related
Utilizing onSignalStrengthsChanged, getAllCellInfo(), and related methods, my app monitors cell signal data and displays the results in realtime. My code works very well when targeting API 28 and lower, automatically refreshing the data as it changes. Targeting API 29 results in some Android 10 devices failing to update the data -- but not all.
I discovered TelephonyManager.requestCellInfoUpdate() was added to API 29, which may(?) be needed to resolve this issue. However, I have been unable to find any information about this method beyond the concise definition on the Android Reference. Does this method need to be used to refresh cell info? Are any code samples or further explanations available?
If that method is not relevant, is there another change in API 29 that could cause this behavior? ACCESS_FINE_LOCATION is confirmed to be granted, which appears to be the only other relevant API change.
I have noticed the same behaviour targeting Android 10 (API Level 29). The only workaround I have found is to regularly poll the API and look for changes.
Example code below:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
tm.requestCellInfoUpdate(minThreadExecutor, new TelephonyManager.CellInfoCallback() {
#Override
public void onCellInfo(#NonNull List<CellInfo> list) {
//Extract needed data
}
});
}
}, 1000, 1000 );
Reading the docs there is a mention of this in the getAllCellInfo() documentation.
Apps targeting Android Q or higher will no longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps will receive the latest cached results, which may not be current. Apps targeting Android Q or higher that wish to request updated CellInfo should call requestCellInfoUpdate(); however, in all cases, updates will be rate-limited and are not guaranteed. To determine the recency of CellInfo data, callers should check CellInfo#getTimeStamp().
So the preference is if you are targeting Android Q or higher, you should be opting for requestCellInfoUpdate()
// 1. Create a TelephonyManager instance
telephonyManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
// 2. Define a CellInfoCallback callback
TelephonyManager.CellInfoCallback cellInfoCallback = new TelephonyManager.CellInfoCallback() {
#Override
public void onCellInfo(List<CellInfo> cellInfo) {
// DO SOMETHING
}
}
// 3. Now you can call the method to DO SOMETHING
telephonyManager.requestCellInfoUpdate(this.getMainExecutor(), cellInfoCallback);
Since Google has announced that chromebook also support "Android Application" so I also wanted to support my app on chromebook although it is running fine with few exception which I need to fix.
I want to write code in such a way that that is will execute only for chromebook and will not execute for android phones and tablet.
I have check with Chromebook documentation in android developer site, I didn't get any such API which tell that your app is running in chrome book environment.
Suggestion from ARC Beta documentation did not work:
If you need to check if your app is running on Chrome OS, look for chromium as the android.os.Build.BRAND and android.os.Build.MANUFACTURER.
Both return google on an ASUS Chromebook.
Finally I figure out a way to know if app in running in ARC:
context.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
Jan 15, 2023 Note-- Jump to the bottom of this answer to read how Google has changed their own method for checking YET AGAIN.
(Or keep reading for the history of the ARC check.)
Another method Google uses in their own code (updated several times now from link) is to check if Build.DEVICE ends with "_cheets". I don't know if ending device names like this is some kind of long-term strategy or a fast workaround, but it's also worth a look in addition to dex's proposed solution.
FWIW, since ARCWelder's method is deprecated and there's no official documentation on this (yet), I've also started a discussion in the XDA forums here for people to discuss what works/doesn't work on various devices.
Update 5/18: Looks like the code above was moved and updated, so Google's new ARC check as of May 2018 is here, particularly in this bit:
... } else if (Build.DEVICE != null && Build.DEVICE.matches(ARC_DEVICE_PATTERN)) {
mFormFactor = FORM_FACTOR_ARC;
} else { ...
where ARC_DEVICE_PATTERN is defined as
private static final String ARC_DEVICE_PATTERN = ".+_cheets|cheets_.+";
So it's not just a device ending with _cheets. It can start with cheets_ as well.
Update 8/26/20 -- As of 7 months ago, the source has been moved around from FormFactors.java to FeatureSupport.java. If you were looking for where it went- here it the code as of today.
public static boolean isArc() {
return (Build.DEVICE != null && Build.DEVICE.matches(".+_cheets|cheets_.+"));
}
The test remains the same.
Jan 15, 2023 -- The code has changed again! isArc() is now built into the FeatureUtil class (see commit here) The current version of isArc() :
/** Returns {#code true} if device is an ARC++ device. */
public static boolean isArc() {
return hasAnySystemFeature(ARC_FEATURE, ARC_DEVICE_MANAGEMENT_FEATURE);
}
Where ARC_FEATURE and ARC_DEVICE_MANAGEMENT_FEATURE are defined like this:
public static final String ARC_FEATURE = "org.chromium.arc";
public static final String ARC_DEVICE_MANAGEMENT_FEATURE = "org.chromium.arc.device_management";
the function hasAnySystemFeature() simply checks individual features and returns true if any is true.
Therefore the following might work as a simple standalone check in kotlin (where context is the activity context):
fun isArc(): Boolean {
return ((context.packageManager.hasSystemFeature("org.chromium.arc")) || (context.packageManager.hasSystemFeature("org.chromium.arc.device_management")))
Note this is similar to #dex's answer below, but includes both tests used by the Android source.
Incidentally, from looking at the code linked above you can also check other device characteristics like like isWatch(), isTV(), isAutomotive(), isPC(), isVrHeadset(), isLowRam(), etc. using similar feature checks.
PackageManager pm = context.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_PC))
// it's a chromebook
I found the solution in Android CTS code.
public static boolean isArc(#NonNull Context context) {
PackageManager pm = context.getPackageManager();
return pm.hasSystemFeature( "org.chromium.arc" ) || pm.hasSystemFeature( "org.chromium.arc.device_management" );
}
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);
in agreement with the recent post from Android Developers http://android-developers.blogspot.pt/2013/10/getting-your-sms-apps-ready-for-kitkat.html ,I was trying to prepare my app to the new android version, but encountered a problem with the part they suggest to create a dialog to let the user set the app as the default application to handle SMS's :
Android Developers Post
public class ComposeSmsActivity extends Activity {
#Override
protected void onResume() {
super.onResume();
final String myPackageName = getPackageName();
if (!Telephony.Sms.getDefaultSmsPackage(this).equals(myPackageName)) {
// App is not default.
// Show the "not currently set as the default SMS app" interface
View viewGroup = findViewById(R.id.not_default_app);
viewGroup.setVisibility(View.VISIBLE);
// Set up a button that allows the user to change the default SMS app
Button button = (Button) findViewById(R.id.change_default_app);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent =
new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME,
myPackageName);
startActivity(intent);
}
});
} else {
// App is the default.
// Hide the "not currently set as the default SMS app" interface
View viewGroup = findViewById(R.id.not_default_app);
viewGroup.setVisibility(View.GONE);
}
}
}
the code itself in pretty much straightforward, but I'm unable to access to Telephony.Sms.getDefaultSmsPackage because it says that Telephony cannot be resolved, and I can't find any import or declaration that would fix that.
Can anyone please help?
android.provider.Telephony simply doesn't exist yet (as of API 18 - 4.3).
This class will be added in 4.4 (presumably API 19), and that blog post is highlighting the changes that you should make once the new API is released so you aren't surprised when the time comes.
From the end of the post:
To help you make the changes, we'll soon be providing the necessary SDK components for Android 4.4 that allow you to compile and test your changes on Android 4.4.
Don't forget that you should wrap this code in an API version check so you don't run into issues with older versions that don't have this class.
this change will break all the SMS blocking apps.
"Note that—beginning with Android 4.4—any attempt by your app to abort the SMS_RECEIVED_ACTION broadcast will be ignored so all apps interested have the chance to receive it."
Do you think there is a way to go around this?!
Maybe at least on Root?
Apparently there is with root access. The latest version Cerberus app claim to be doing this.
Now, if only I knew how they do it :(
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
I write code like this, I used the sdk4.0 to compile this android program, so it didn't cause compile error. When I run this program in my phone that running android 2.3.4, it run well.
Why? I am confused that version 2.3.4 (api level 10) has Build.VERSION_CODES.ICE_CREAM_SANDWICH property? And when I used sdk2.3.4 will cause compile error.
More
I test some code like these below,
private ScaleGestureDetector mScaleGestureDetector;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
mScaleGestureDetector = new ScaleGestureDetector(this, new MyOnScaleGestureListener());
}
this code will run well on android 1.6 api level 4, but
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
Log.d(TAG, "getx(0): " + event.getX(0));
}
this program run failed on android 1.6 api level 4.
They both run on android 2.3.4 well.
why? (In ScaleGestureDetector class use the event.getX(0) (since api level 5) too)
I test some code more..
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Fragment f = new Fragment();
}
When I run it on android 1.6 emulator it throw java.lang.VerifyError, but on my phone running android 2.3.4 it throws java.lang.NoClassDefFoundError.
Why??
It's not as strange as it seems. It has to do with how Java treat constant values of primitives. During compilation, the value of the constant is put in the byte code, not a reference to the actual constant.
For example:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
will actucally be translated by the compiler to:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + 14);
so the reference to the actual constant (and class) is removed.
Regarding the code that doesn't run for you, it has to do with that the MotionEvent.getX(int n) method wasn't available until api level 5. Before that, multitouch wasn't supported and thus no need for any other method than getX().
It doesn't matter if you actually call the method that doesn't exist. The error is appearing while the class is being loaded and verified by the platform. You most likely get a VerifyError in the log, since it will discover that you're trying to call a non-existent method during the verification.
On the other hand, if you try to use a class that doesn't exist, you will get a ClassNotFoundException instead. Note that sometimes, a class exists in Android even if the documentation doesn't say so. Some classes existed in early versions of Android but weren't exposed until later. Some have even gone the other way.
So:
Trying to use a class that doesn't exist - ClassNotFoundException
Trying to use a method that doesn't exist on a class that exists - VerifyError
(When it comes to using Fragments, they are available for earlier versions with the standalone Android Support Library)