I have a requirement of reading incoming SMS from a few of the e-commerce apps. For that, I added BroadcastReceiver for receiving SMS and reading that. Also added runtime permission of READ_SMS for that, done setting a priority of 1000 for that receiver. I tested it for a few days sending a few dummy messages, along with the eCommerce app messages similar to -
Delivered: Your package with Macbook Air
... has been successfully delivered. More info
at http://amzn.in/bAieP6f
Your SnapDeal order AWB:12791911327207 is delivered on 19-02-2020 at
16:20 by Xpressbees received by Username. You may contact us on
020-49116100.
Delivered: Gillette Sensitive Ski... from flipkart.com was delivered.
Click here to give feedback: http://fkrt.it/u33XFQHHHH
And so on.
But after testing for a few days, around 3-4 days, the app suddenly stopped working to read those and any other messages.
Note: The device I am using is - MI A1, with the Android 9 (Pie) version.
The code for the same, I used is as follows -
SmsListener.java (Broadcast Receiver class)
public class SmsListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d("TAG","msg receiver entered");
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
String messageBody = "";
String msg_from = "";
for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
msg_from = smsMessage.getServiceCenterAddress();
Log.d("TAG","msg_from = "+msg_from);
Log.d("TAG","msgBody = "+messageBody);
}
}
}
}
AndroidManifest.xml
a) necessary permissions
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
b) receiver entry
<receiver android:name=".receiver.SmsListener"
>
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
I also tried to modify the priority to 999, as suggested in a few other StackOverflow answers to a similar query, but no luck.
Though, the same code is still working in the demo app, but unluckily not in my app.
I also tried using EventBus referring here. That too worked for some time, unless I again tried testing using
Your SnapDeal order AWB:12791911327207 is delivered on 19-02-2020 at
16:20 by Xpressbees received by Username. You may contact us on
020-49116100.
Don't know what's wrong, as the code looks fine, and was working fine in the same app, also the same code working fine in another demo app.
I also found a suggestion to whitelist the App in this answer. Though, don't know how to do that or whether its the perfect solution.
Please suggest how to achieve reading incoming SMS, or what I am missing or going wrong. Thanks.
Finally, made it working in higher versions too, just by adding
android:permission="android.permission.BROADCAST_SMS"
in the receiver tag in AndroidManifest.xml
and made it something like -
<receiver android:name=".receiver.SmsListener"
android:permission="android.permission.BROADCAST_SMS"
>
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
In my first application, I define a custom permission and an implicit BroadcastReceiver in manifest file:
<permission
android:name="com.example.test.TEST"
android:protectionLevel="signature" />
<receiver
android:name=".TestBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:permission="com.example.test.TEST">
<intent-filter>
<action android:name="com.example.test.TEST_RECEIVER" />
</intent-filter>
</receiver>
And this is the TestBroadcastReceiver.java:
public class TestBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("Test", "Hello World!");
Toast.makeText(context, "Hello World!", Toast.LENGTH_LONG).show();
}
}
In my second app, I've added the permission in manifest file:
<uses-permission android:name="com.example.test.TEST" />
And here, I send the broadcast:
getActivity().sendBroadcast(new Intent("com.example.test.TEST_RECEIVER"));
But nothing is called in first app. I know we can't use implicit broadcast in android O and above but according to here, there is an exception for broadcasts that require a signature permission:
Broadcasts that require a signature permission are exempted from this
restriction, since these broadcasts are only sent to apps that are
signed with the same certificate, not to all the apps on the device.
So how can I signal my other apps in android O?
According to CommonsWare answer, the problem is that I was missing setPackage() part. So I changed the code as below and now broadcast is received:
getActivity().sendBroadcast(new Intent("com.example.test.TEST_RECEIVER").setPackage("com.example.test"));
I have already checked all the related questions and have not found any solution for this problem. So this is an absolutely new problem for me.
What I Have
I have an Android app which registers a few broadcast receivers in its manifest. This is what my manifest looks like.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.app.myapp">
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-feature
android:name="android.hardware.screen.portrait"
android:required="false" />
<application
android:name=".base.MyApp"
android:allowBackup="false"
android:icon="#drawable/ic_launcher"
android:label="#string/label_app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:replace="label, allowBackup">
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver android:name=".PhoneCallReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<receiver
android:name=".mics.DeviceAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/device_admin" />
</receiver>
<receiver
android:name="com.clevertap.android.sdk.InstallReferrerBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<meta-data
android:name="com.app.myapp.utils.ImageLoaderModule"
android:value="GlideModule" />
<meta-data
android:name="com.app.myapp.utils.AudioCoverLoaderModule"
android:value="GlideModule" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
<activity
android:name=".core.activities.SplashActivity"
android:excludeFromRecents="true"
android:label="#string/label_app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity-alias
android:name=".core.activities.SplashActivity-Alias"
android:icon="#drawable/ic_launcher"
android:label="#string/label_app_name"
android:noHistory="true"
android:targetActivity="com.app.myapp.core.activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY" />
</intent-filter>
</activity-alias>
<activity
android:name=".core.flow.authFlow.activities.AuthFlowActivity"
android:excludeFromRecents="true"
android:label="#string/label_app_name"
android:screenOrientation="portrait" />
<service android:name=".features.fileCloudSync.KillNotificationService" />
</application>
</manifest>
There are 10-15 other activities as well but have been removed for simplicity. And this is the basic boot receiver class. I start a service from here.
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
AlertUtils.showToast(context, "BOOT COMPLETED", Toast.LENGTH_LONG);
}
}
}
and the phone call receiver class looks something like this (it has been simplified as well),
public class PhoneCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
AlertUtils.showToast(context, "PHONE CALL RECEIVED", Toast.LENGTH_LONG);
// Simplified for brevity
}
}
}
The Problem
All these receivers work fine when I install the app and start it once. But after I reboot my device these receivers don't work at all. Neither the BootCompleteReceiver nor the PhoneCallReceiver gets their onReceive() method called.
My assumption was that these receivers would get registered automatically after reboot, but it just doesn't work. I need the BootCompleteReceiver to work so that I can start an important service in my app.
My Observations
I have tested this thoroughly. After rebooting the device, the receivers work fine in my Nexus 5X (Nougat), Nexus 6P (Nougat), YU Yuphoria (Lollipop) but not in my OnePlus 3 (Nougat) and Mi 4i (Lollipop).
How can the same code work perfectly on a few devices and not work at all on the other devices? I haven't changed anything at all.
What am I doing wrong here? My app is heavily dependent on these broadcasts and starts services based on these. Any help will be highly appreciated.
EDIT 1
To understand the problem better, I just created a very small test project with just a single activity and the exact same BootCompleteReceiver and PhoneCallReceiver.
But weirdly, this project works perfectly on my OnePlus 3 where my actual app's receivers don't work after a reboot. I was initially assuming that the problem is in the OS or the device somehow, but it is not.
So where is the actual problem? Is it in my app (but it works perfectly on other devices) or in the OS and device (the small test project works fine on the same OS and same device)?
It is really confusing to me. I would need some expert help on this.
EDIT 2
I have tried the suggestion given by #shadygoneinsane. Here are my observations.
1) I tried to send the BOOT_COMPLETED broadcast via ADB.
./adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.app.myapp
And I got this stack trace,
Broadcasting: Intent { act=android.intent.action.BOOT_COMPLETED pkg=com.app.myapp }
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.BOOT_COMPLETED from pid=25378, uid=2000
at android.os.Parcel.readException(Parcel.java:1683)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.broadcastIntent(ActivityManagerNative.java:3696)
at com.android.commands.am.Am.sendBroadcast(Am.java:778)
at com.android.commands.am.Am.onRun(Am.java:404)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
at com.android.commands.am.Am.main(Am.java:121)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:276)
Maybe because my device is not rooted. I am unable to send this broadcast in any way.
2) I tried with the PROCESS_OUTGOING_CALLS broadcast after that.
./adb shell am broadcast -a android.intent.action.PROCESS_OUTGOING_CALLS -p com.app.myapp
I got this,
Broadcasting: Intent { act=android.intent.action.PROCESS_OUTGOING_CALLS pkg=com.app.myapp }
Broadcast completed: result=0
It seems that the broadcast was successful, but I do not see any Toast or any log. I then opened my dialer to dial a number and I can then see the Toast and the log both.
So it seems that sending the broadcast via ADB didn't work, but actually opening the dialer and dialing a number did.
EDIT 3
As per the suggestion from #ChaitanyaAtkuri, I have also tried adding priority to the intent-filters but that didn't work as well.
I have used priorities like 500, 999 and even the highest integer value, but nothing works. This problem is also occurring in some of my friends apps as well. They work in some devices and doesn't work in others.
EDIT 4
I have finally found out the root cause of the problem happening in my OnePlus 3. My OnePlus 3 recently got updated to Nougat and they introduced a feature similar to Mi devices which prevent certain apps from auto-starting after reboot.
Upon disabling this feature my app started receiving broadcasts after reboot perfectly. But this still doesn't explain two things.
1) My small test project is whitelisted automatically in the list of AutoLaunch apps and that is why it works as expected. But how is this possible? Why the OS considers this small app worthy to be auto-started?
2) There are some apps like LockDown Pro, 500 Firepaper which is blacklisted in the AutoLaunch apps screen but still, it receives broadcasts after reboot in my OnePlus 3 and Mi 4i. How is that possible now? Is it somehow possible to programmatically allow my app to auto launch in these devices (OnePlus and Mi)?
EDIT 5
I have tried the solution proposed by #Rahul Chowdhury and it really seems to work very well. After adding the accessibility service the problem is re-solved.
But if the user revokes the accessibility permission after granting it then is there a way for me to programmatically check if the accessibility permission is available to my app?
Here's a tested and working solution on both the devices that you mentioned, OnePlus and Mi.
As you said the auto-start prevention feature on OnePlus and Mi devices prevent apps from starting up their services automatically on boot complete so as to improve the overall device boot speed and battery performance. However, there's a workaround to get your app working even when this feature is turned on.
I have noticed that if you have an AccessibilityService in your app and it is turned on by the user, then your app passes the filter that these manufacturers apply and the app receives it's boot complete event and any other BroadcastReceiver works as expected.
The possible explanation of this trick can be that since AccessibilityService is a system level service, so by registering your own service you are passing the certain filter applied by these manufacturers and as soon as your custom AccessibilityService gets triggered by the OS, your app becomes active in receiving the eligible BroadcastReceiver that you had registered.
So, here's how to do it,
Start by adding this permission to your AndroidManifest.xml,
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>
This will allow you to register your app's AccessibilityService with the system.
Now, add a very basic configuration for your AccessibilityService by creating a file for example my_accessibility_service.xml inside XML folder under your res folder in your project.
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackSpoken"
android:description="#string/service_desc"
android:notificationTimeout="100"/>
There's just one more step left to do, define your custom AccessibilityService in your project,
public class MyAccessibilityService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) { }
#Override
public void onInterrupt() {
}
}
Note, since you're not needing the AccessibilityService for any purpose rather than this workaround, you can leave the overridden methods empty.
Finally, just declare your AccessibilityService in your AndroidManifest.xml,
<service
android:name=".MyAccessibilityService"
android:label="#string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/my_accessibility_service"/>
</service>
That's all. Now within your app, just ask your users to turn on the accessibility service for your app from the settings and leave it on and voila! Your app works fine on all devices even where the OS puts a filter on which apps should auto-start on boot.
EDIT 1
Here's how you can check if accessibility service is turned ON or not for your app,
private static final int ACCESSIBILITY_ENABLED = 1;
public static boolean isAccessibilitySettingsOn(Context context) {
int accessibilityEnabled = 0;
final String service = context.getPackageName() + "/" + MyAccessibilityService.class.getCanonicalName();
try {
accessibilityEnabled = Settings.Secure.getInt(
context.getApplicationContext().getContentResolver(),
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException e) {
Log.e("AU", "Error finding setting, default accessibility to not found: "
+ e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled == ACCESSIBILITY_ENABLED) {
String settingValue = Settings.Secure.getString(
context.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(service)) {
return true;
}
}
}
}
return false;
}
Hope this helps.
Hi I am late to the party but I was following this question from it's start. I know that One-plus and some other OEMs maintain a list of apps which can receive BOOT_COMPLETED broadcast. If your app is not white listed then your app won't be started on boot. Now I've a solution which is very efficient in terms of memory and resources and guaranteed to start your task or service after reboot or hard boot also does not need AccessibilityService as proposed in this answer. Here it goes..
Add the follwing permission in your manifest file
2.If you don't have a dependency on com.google.android.gms:play-services-gcm, add the following to your build.gradle's dependencies section:
compile 'com.firebase:firebase-jobdispatcher:0.5.2'
Otherwise add the following:
compile 'com.firebase:firebase-jobdispatcher-with-gcm-dep:0.5.2'
This is a library from firebase team which depends on google-play-service library to schedule your jobs and from my point of view google-play-service has the permission to start at boot so instead of system ,google-play-service will run your job as soon as device rebooted.
Now this step is easy Just define a JobService class
public class MyJobService extends JobService {
#Override
public boolean onStartJob(JobParameters job) {
Log.v("Running", "====>>>>MyJobService");
return false; // Answers the question: "Is there still work going on?"
}
#Override
public boolean onStopJob(JobParameters job) {
Log.v("Stopping", "====>>>>MyJobService");
return true; // Answers the question: "Should this job be retried?"
}
}
Add your Job Service in manifest file.
Schedule this job anywhere you want for e.g when your app start.
FirebaseJobDispatcher dispatcher =
new FirebaseJobDispatcher(new GooglePlayDriver(getApplicationContext()));
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(MyJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag-test")
// repeat the job
.setRecurring(true)
// persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(0, 60))
// don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setExtras(myExtrasBundle)
.build();
dispatcher.mustSchedule(myJob);
6.That's it!! Now you can execute your task or service on device boot no matter you are in white list or not.
There is one point to note that Google Play Service must be installed on device otherwise it won't work.
#Aritra, Try this
<receiver
android:name=".mics.BootReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="500" >
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Remove quickBoot intent filter and try running it, as per the documentation we only required BootCompleted to acheive it. May be it is interrupting this.
Also one more important point to Note :
Don't rely completely or test on Mi devices as they have their own OS which halts the basic features of Android, like they stop the Push notifications services and background services just to optimize battery usage. To test this on Mi device, mark your app as "AutoStart" in Security app and then try.
You can ask user for autostart permission, and direct them to the required settings page:
private void autoStart() {
try {
Intent intent = new Intent();
String manufacturer = android.os.Build.MANUFACTURER;
if ("xiaomi".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
} else if ("oppo".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
} else if ("vivo".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
} else if ("Letv".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
} else if ("Honor".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
} else if ("oneplus".equalsIgnoreCase(manufacturer)) {
intent.setComponent(new ComponentName("com.oneplus.security", "com.oneplus.security.chainlaunch.view.ChainLaunchAppListAct‌​ivity"));
}
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
startActivity(intent);
}
} catch (Exception e) {
Log.e("exc", String.valueOf(e));
}
}
After doing this, receiver always got triggered on reboot.
The way IntentFilters work is that each <intent-filter></intent-filter> contains one way of firing up the component. If you have multiple ways of firing it up (like two actions that you want to listen to in one BroadcastReceiver), you'll need an independent <intent-filter></intent-filter> definition for each.
Hence, you can try changing:
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
to:
<receiver android:name=".mics.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
Read more here: Intents and Intent Filters | Android Developers
EDIT
If it still doesn't work, you can try to test if your manifest declaration is done correctly. Try executing the following command in your terminal, keeping the test device connected to the computer:
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.app.myapp/.mics.BootReceiver
If this doesn't work, you should recheck the relative package declaration of the receiver in your manifest file.
EDIT 2
It may sound weird but try following these steps:
Uninstall the app from your phone (ensure it is uninstalled for all users)
Reboot your phone
Clean the project
Build and run the project in your device again
I have been struggling with this issue from almost a year. In all my apps, I show a notice to users to disable battery optimization for my app.
After a lot of testing on One Plus devices, I am able to receive boot completed broadcast when battery optimization is turned off for my app. In my opinion, it is much better than the accessibility service discussed above.
The simplest way to ask the user to disable the battery optimization for your app is to show some kind of notice, and open the battery optimization page when the user clicks on it. You can use the below code to do that.
public void openPowerSettings(View v) {
/* Make Sure to add below code to manifest
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
*/
try {
Intent i = new Intent(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivityForResult(i, 1);
} catch (Exception e) {
Log.e (TAG, "Exception: " + e.toString());
}
}
And you can also hide the notice if below function returns true.
public static boolean is_ignoring_battery_optimizations(Context context) {
String PACKAGE_NAME = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(context.POWER_SERVICE);
boolean status = true;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
status = pm.isIgnoringBatteryOptimizations(PACKAGE_NAME);
}
return status;
}
How to start service on device boot(autorun app, etc.)
For first: since version Android 3.1+ you don't receive BOOT_COMPLETE if user never started your app at least once or user "force closed" application.
This was done to prevent malware automatically register service. This security hole was closed in newer versions of Android.
Solution:
Create app with activity. When user run it once app can receive BOOT_COMPLETE broadcast message.
For second: BOOT_COMPLETE is sent before external storage is mounted. If app is installed to external storage it won't receive BOOT_COMPLETE broadcast message.
In this case there is two solution:
Install your app to internal storage
Install another small app in internal storage. This app receives BOOT_COMPLETE and run second app on external storage.
If your app already installed in internal storage then code below can help you understand how to start service on device boot.
In Manifest.xml
Permission:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Register your BOOT_COMPLETED receiver:
<receiver android:name="org.yourapp.OnBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Register your service:
<service android:name="org.yourapp.YourCoolService" />
In receiver OnBoot.java:
public class OnBoot extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// Create Intent
Intent serviceIntent = new Intent(context, YourCoolService.class);
// Start service
context.startService(serviceIntent);
}
}
For HTC you maybe need also add in Manifest this code if device don't catch RECEIVE_BOOT_COMPLETED:
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
Receiver now look like this:
<receiver android:name="org.yourapp.OnBoot">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
How to test BOOT_COMPLETED without restart emulator or real device?
It's easy. Try this:
adb -s device-or-emulator-id shell am broadcast -a android.intent.action.BOOT_COMPLETED
How to get device id? Get list of connected devices with id's:
adb devices
adb in ADT by default you can find in:
adt-installation-dir/sdk/platform-tools
Enjoy! )
Is there anyways in sending a location based push notification for android devices with out using a third party push notification service such as Parse? I would like to send push notification to my users without the annoyance of getting a notification that doesn't relate to that specific user because they are not in a certain area. Also, I could get the users location based on a time interval but I would rather do it a different way then that, if possible.
Yes, this is entirely possible, as long as I'm correctly interpreting what you are asking.
To accomplish this, you would send the GCM push notification to all of your users (unless you had a way, server-side of filtering some of them out). Then in your application, instead of just creating a Notification and passing it to the notification manager, you would first use the LocationManager (or the newer LocationServices API) to determine if the user is within the proper location first, and then just discard the GCM notification if they're not.
You'll need to take care of several things in order to do this:
Your AndroidManifest.xml will require several permission changes, both for the GCM changes, and for the Location access:
<!-- Needed for processing notifications -->
<permission android:name="com.myappname.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.myappname.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Needed for Location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
You'll also need to set up a Notification Receiver in the <application> section of the manifest:
<receiver android:name="com.myappname.NotificationReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.myappname" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.myappname" />
</intent-filter>
</receiver>
In addition, you'll need write your NotificationReceiver java class, and override the onReceive function:
public class NotificationReceiver extends BroadcastReceiver {
public void onReceive(final Context context, final Intent intent) {
if ("com.google.android.c2dm.intent.REGISTRATION".equals(intent.getAction())) {
handleRegistration(context, intent); // you'll have to write this function
} else if ("com.google.android.c2dm.intent.RECEIVE".equals(intent.getAction())) {
// the handle message function will need to check the user's current location using the location API you choose, and then create the proper Notification if necessary.
handleMessage(context, intent);
}
}
I am trying to set up a Google App engine server endpoint that sends Google Cloud Messages to android devices. So far, the device registers using GoogleCloudMessaging.register() and sends the generated regID to the server. The server then can send a message to the regIDs it has saved. Through the log messages I can see that the regIDs are all arriving correctly, and the server is sending messages correctly. The devices though aren't getting any messages.
This is the receiver in the manifest:
<receiver
android:name="---.app.MessageReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.permission.RECEIVE" />
<category android:name="---.app" />
</intent-filter>
</receiver>
This is the code for the receiver:
public class MessageReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("---", "RECIVED A MESSAGE!!!!!!");
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GCMReceiverService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
The service never gets started, and the log message here is never shown.
Is there something I'm missing?
EDIT - these are the permissions:
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="---.app.permission.C2D_MESSAGE" />
<permission
android:name="---.app.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
There is a working solution on SO Android GCM basic implementation
The author edited his post, so you should just take it as an example. Basically, without correct permissions set AND correct Broadcast's constructor used, android won't give a damn about messages sent to an application.
Please set your permissions and other things accordingly and try again.
There is really nice post later down the comments, way under accepted one.
Really stupid mistake. Instead of .intent.RECIEVE in the intent filter it was .permission.RECEIVE.