Schedule tasks with AlarmManager on Android

Receiver Class

public class MyAlarmReceiver extends BroadcastReceiver {

    private AlarmManager alarmManager;
    private PowerManager powerManager;
    private PowerManager.WakeLock wakeLock;

    @Override
    public void onReceive(Context context, Intent intent) {

        try {
            powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TimeTickWakeLog");
            wakeLock.acquire(5 * 1000);

            MyLog.d("alarm manager : begin onReceive");
        } catch (Exception ex) {
            ex.printStackTrace();
            MyLog.ex(ex);

            if (wakeLock != null) {
                wakeLock.release();
                powerManager = null;
                wakeLock = null;
            }
        }

        try {
            int isServiceRunning = 0;
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
                if (MyMqttService.class.getName().equals(service.service.getClassName())) {

                    // service is running
                    isServiceRunning++;
                }
            }

            if (isServiceRunning == 0) {
                // service is not running, start it
                Intent serviceIntent = new Intent(context, MyMqttService.class);
                context.startService(serviceIntent);
            }

            // connect to mqtt broker if needed
            if (Statics.isAppInBackground()) {

                EventBus.getDefault().post(new ReconnectMqttEvent());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            MyLog.ex(ex);
        }


        // the reason for separating try/catches is to insure next alarm manager will be scheduled
        try {
            // region reschedule alarm manager
            long timeToWait = 60 * 1000;

            alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            Intent myIntent = new Intent(context, MyAlarmReceiver.class);
            PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() + timeToWait, alarmIntent);
            } else {
                alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() + timeToWait, alarmIntent);
            }
            // endregion


            MyLog.d("alarm manager : end onReceive");

            if (wakeLock != null) {
                wakeLock.release();
                powerManager = null;
                wakeLock = null;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            MyLog.ex(ex);

            if (wakeLock != null) {
                wakeLock.release();
                powerManager = null;
                wakeLock = null;
            }
        }
    }
}

Manifest file

        <receiver android:name=".MyAlarmReceiver">

        </receiver>

onCreate of Service

        // region schedule alarm manager for the first time

        long timeToWait = 60 * 1000;

        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(this, MyAlarmReceiver.class);
        PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    SystemClock.elapsedRealtime() + timeToWait, alarmIntent);
        } else {
            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    SystemClock.elapsedRealtime() + timeToWait, alarmIntent);
        }
        // endregion

Rereferences
https://developer.android.com/training/scheduling/alarms
https://stackoverflow.com/questions/4459058/alarm-manager-example
https://www.javatpoint.com/android-alarmmanager

Listening to android.os.action.DEVICE_IDLE_MODE_CHANGED

public class IdleModeReceiver extends BroadcastReceiver {

    private PowerManager powerManager;
    private PowerManager.WakeLock wakeLock;

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TimeTickWakeLog");
            wakeLock.acquire(30 * 1000);

            if (powerManager.isDeviceIdleMode()) {
                // the device is now in doze mode

                MyLog.d("power manager : doze mode");
            }
            else if (powerManager.isInteractive()){

                // the device is in iteractive mode

                MyLog.d("power manager : interactive mode");

            }
            else if (powerManager.isPowerSaveMode()) {
                // the device is in power save mode
                MyLog.d("power manager : power save mode");
            }

            if (wakeLock != null) {
                wakeLock.release();
                powerManager = null;
                wakeLock = null;
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            MyLog.ex(ex);
            if (wakeLock != null) {
                wakeLock.release();
                powerManager = null;
                wakeLock = null;
            }
        }

    }
}

for older devices add to manifest file:

        <receiver android:name=".IdleModeReceiver">
            <intent-filter>
                <action android:name=" android.os.action.DEVICE_IDLE_MODE_CHANGED" />
            </intent-filter>
        </receiver>

for new devices:

private IdleModeReceiver idleModeReceiver = new IdleModeReceiver();

        // register idle change
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            IntentFilter filter = new IntentFilter();
            filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);

            registerReceiver(idleModeReceiver, filter);
        }

References
https://developer.android.com/reference/android/os/PowerManager
https://stackoverflow.com/questions/31559698/android-m-listening-to-android-os-action-device-idle-mode-changed

Optimize for Doze and App Standby

    private boolean isIntentAvailable(@NonNull Context context, @NonNull Intent intent) {
        return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
    }
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
                String packageName = getPackageName();
                Intent batteryIgnoreIntent = new Intent("android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS");
                batteryIgnoreIntent.setData(Uri.parse("package:" + packageName));
                boolean isIgnoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(packageName);

                if (!isIgnoringBatteryOptimizations) {

                    if (isIntentAvailable(MainActivity.this, batteryIgnoreIntent)) {
                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                        intent.setData(Uri.parse("package:" + packageName));
                        startActivity(intent);
                    } else {
                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
                        startActivity(intent);
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            MyLog.ex(ex);
        }

Testing your app with Doze

adb shell dumpsys deviceidle enable

Force the system into idle mode by running the following command:

adb shell dumpsys deviceidle force-idle

When ready, exit idle mode by running the following command:

adb shell dumpsys deviceidle unforce

Force the app into App Standby mode by running the following commands:

adb shell dumpsys battery unplug
adb shell am set-inactive <packageName> true

Simulate waking your app using the following commands:

adb shell am set-inactive <packageName> false
adb shell am get-inactive <packageName>

References
https://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/
https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
https://developer.android.com/topic/performance/scheduling
https://www.bignerdranch.com/blog/diving-into-doze-mode-for-developers/
https://stackoverflow.com/questions/48805376/how-solve-a-fatal-exception-in-android-content-activitynotfoundexception-error
https://stackoverflow.com/questions/32627342/how-to-whitelist-app-in-doze-mode-android-6-0
https://stackoverflow.com/questions/36457524/can-not-switch-to-doze-mode
https://stackoverflow.com/questions/33709046/wake-lock-disabled-in-foreground-service-with-doze-mode-new-battery-optimizati

Start Android application on boot

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver android:name="MyStartServiceReceiver" >
   <intent-filter>
      <action android:name="android.intent.action.BOOT_COMPLETED" />
   </intent-filter>
</receiver>
public class MyStartServiceReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        try {
            if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
                Intent serviceIntent = new Intent(context, MyMqttService.class);
                context.startService(serviceIntent);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            MyLog.ex(ex);
        }
    }
}

Keep the device awake on Android using PowerManager

<uses-permission android:name="android.permission.WAKE_LOCK" />
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
        "MyWakelockTag");
wakeLock.acquire();
wakelock.release()

keep wakeLock for whole app

public class MyApp extends Application 
{
    private static final String TAG = MyApp.class.getSimpleName();
    private PowerManager.WakeLock mWakeLock = null;

    @Override
    public void onCreate() {
        super.onCreate();

        final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        mWakeLock.acquire();
    }

    @Override
    public void onTerminate() {
        if (mWakeLock.isHeld())
            mWakeLock.release();
        super.onTerminate();
    }
}

or

Use a broadcast receiver that keeps the device awake

References
https://developer.android.com/training/scheduling/wakelock
https://developer.android.com/topic/performance/vitals/wakelock
https://stackoverflow.com/questions/9000563/partial-wake-lock-is-not-working

Modify Socket parameters of Mqtt Client

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.net.SocketFactory;

public class MySocketFactory extends SocketFactory {

    private SocketFactory socketFactory;
    private Socket socket;

    public MySocketFactory() {
        this.socketFactory = SocketFactory.getDefault();
    }

    public MySocketFactory(SocketFactory socketFactory) {
        this.socketFactory = socketFactory;
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        socket = this.socketFactory.createSocket(host, port);
        modifySocket();
        return socket;
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
        socket = socketFactory.createSocket(host, port, localHost, localPort);
        modifySocket();
        return socket;
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        socket = socketFactory.createSocket(host, port);
        modifySocket();
        return socket;
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        socket = socketFactory.createSocket(address, port, localAddress, localPort);
        modifySocket();
        return socket;
    }

    @Override
    public Socket createSocket() throws IOException {
        socket = socketFactory.createSocket();
        modifySocket();
        return socket;
    }

    private void modifySocket() throws IOException {
        socket.setKeepAlive(true);
        socket.setSoTimeout(10 * 1000);
        socket.setSoLinger(true, 0);
        socket.setTcpNoDelay(true);
    }
}
String broker = Statics.broker;
// mqtt paho client id
clientId = MqttClient.generateClientId();
client = new MqttAndroidClient(MyMqttService.this, broker,
                        clientId, new MemoryPersistence());

MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(false);
options.setCleanSession(true);
options.setUserName(""test");
options.setPassword("test".toCharArray());
options.setConnectionTimeout(20);
options.setKeepAliveInterval(0);

MySocketFactory mySocketFactory=new MySocketFactory();
options.setSocketFactory(mySocketFactory);

Saving Android Activity state using Save Instance State

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

References
https://stackoverflow.com/questions/151777/saving-android-activity-state-using-save-instance-state

Android Notification with Sound and Vibratation

            Notification.Builder builder = new Notification.Builder(this)
                    .setContentTitle("مانیتورینگ")
                    .setContentText(content)
                    .setSmallIcon(R.drawable.ic_monitoring)
                    .setContentIntent(pIntent)
                    .setOngoing(true);
            //.setPriority(Notification.PRIORITY_MAX)

            if (count > 0) {
                builder.setSound(alarmSound);
                builder.setOnlyAlertOnce(true);
                builder.setLights(Color.RED, 500, 500);
                // 0 : Start without a delay
                // 400 : Vibrate for 400 milliseconds
                // 200 : Pause for 200 milliseconds
                // 400 : Vibrate for 400 milliseconds
                long[] pattern = new long[]{0, 400, 200, 400};
                builder.setVibrate(pattern);
            }

            Notification notification = builder.build();

References
https://stackoverflow.com/questions/15809399/android-notification-sound
https://gist.github.com/nieldeokar/e05fffe4d639dfabf0d57e96cb8055e2