'Android Java, Vibrator doesn't work from BroadcastReceiver when the app is not active state

I'm writing an application which receives specific sms messages like '0000' as 'play command', and '9999' as 'reset'. When '0000' comes, it'll start vibrator, and if SMS message '9999' comes, vibrator will be stopped.

To keep application context I made MyApp class like below.

public class MyApp extends Application {
private static MyApp instance;

public static MyApp getInstance() {
    return instance;
}

public static Context getContext(){
    return instance;
    // or return instance.getApplicationContext();
}

@Override
public void onCreate() {
    instance = this;
    super.onCreate();
}
}

In 'AndroidManifest.xml', I have added relative permissions like below.

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

Added MyApp like below.

    <application
    android:name=".MyApp"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.RemoteController">
    ......
    </application>

And added 'SMS receiver' like below.

    <receiver
        android:name=".SmsReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter android:priority="1">
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:label="@string/app_name"
        android:theme="@style/Theme.RemoteController.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

In 'MainActivity.java', I added the following code.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ......
    requirePerms();
}
private void requirePerms(){
    String[] permissions = {
            Manifest.permission.RECEIVE_SMS,
            Manifest.permission.VIBRATE
    };
    ActivityCompat.requestPermissions(this, permissions, 1);
}

To get SMS broadcast message, I made SmsReceiver class like below.

public class SmsReceiver extends BroadcastReceiver {

private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
private static final String TAG = "SmsReceiver";

private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(SMS_RECEIVED)) {
        Bundle bundle = intent.getExtras();
        SmsMessage[] messages = parseSmsMessage(bundle);
        if (messages.length > 0) {
            String sender = messages[0].getOriginatingAddress();
            String contents = messages[0].getMessageBody().toString();
            Date receivedDate = new Date(messages[0].getTimestampMillis());
            if (contents.equalsIgnoreCase("0000")) {
                Log.d(TAG, "onReceive : PLAY COMMAND COMES!");
                vibratorStart();
            } else if (contents.equalsIgnoreCase("9999")) {
                Log.d(TAG, "onReceive : RESET COMMAND COMES!");
                // cancel vibrator
            }
            Toast.makeText(context.getApplicationContext(), "MSG : " + contents, Toast.LENGTH_SHORT).show();
        }
    }
}
private static void vibratorStart() {
    Vibrator vibrator = (Vibrator) MyApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
    final long[] vibratePattern = new long[]{1000, 1000, 1000, 1000};
    final int repeat = 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        vibrator.vibrate(VibrationEffect.createWaveform(vibratePattern, repeat));
    } else {
        vibrator.vibrate(vibratePattern, repeat);
    }
}

private SmsMessage[] parseSmsMessage(Bundle bundle) {
    Object[] objs = (Object[]) bundle.get("pdus");
    SmsMessage[] messages = new SmsMessage[objs.length];

    for (int i = 0; i < objs.length; i++) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String format = bundle.getString("format");
            messages[i] = SmsMessage.createFromPdu((byte[]) objs[i], format);
        } else {
            messages[i] = SmsMessage.createFromPdu((byte[]) objs[i]);
        }
    }
    return messages;
}
}

When I test it, it works well if I test it when the application is launched in foreground. But, when it the application is not running or runs in background, it does not work. In detail, it seems that SMS message is received by broadcast and 'vibratorStart()' method is called, and 'vibrator.vibrate()' method is called. But the device's vibrator doesn't start at all.

What should I do to resolve this problem?



Solution 1:[1]

I can not find any solution to resolve this problem. I mean staring vibration in the context of BroadcastReceiver's onReceive() method, there's no way to make the vibrator start. So I changed the point starting vibrator to Forground service instead of BroadcastReceiver. Then it works well.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Geobongpapa