Search code examples
javaandroidbroadcastreceiveralarmmanagerdevice-admin

Method involving AlarmManager isn't functioning the way I need it to


I have a method named lock that sets a random lock screen password and locks the screen using DevicePolicyManager whenever a button is pressed. Right before the phone locks, this lock method also schedules an alarm to go off after a certain amount of time. This alarm calls a method to reset the lock screen password to a stored value.

The issue I am having is whenever the button to call my lock method is pressed, the phone locks like it should, and it also resets the locks screen password to a random one like it should, so that after the phone locks the user can try to unlock it and the lock screen pops up that only accepts the random password. However if the user presses back at the lock screen and then tries to unlock the phone again, the lock screen password is set right away to the stored pin value. This also happens if the user locks the phone again and tries to unlock it. I want the password to be reset after a certain amount of time has passed, not right away.

I have already configured a DeviceAdmin receiver in another class also.

Am I missing something from my AlarmManager or doing something wrong in my lock method? Please note, I am changing the actual device's lock screen password with this method, I am not making my own lock screen.

The lock method in question called when my button is pressed

final Random rand=new Random();

public void lock(View v) {
int diceRoll;

    setMins();

    //this section schedules an alarm that calls my overridden broadcast receiver in this class
    Intent alarmIntent=new Intent(this, AlarmBroadCastReceiver.class);
    PendingIntent pendIntent=PendingIntent.getBroadcast(this, 1, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    alMgr=(AlarmManager) getSystemService(Context.ALARM_SERVICE);
    alMgr.cancel(pendIntent);
    alMgr.set(AlarmManager.ELAPSED_REALTIME, (time * 60000), pendIntent);

    diceRoll=rand.nextInt(9999)+1;

    if (mgr.isAdminActive(cn))
     {
        mgr.setPasswordQuality(cn, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
        mgr.setPasswordMinimumLength(cn, 0);
         //this sets the lock screen password to a random one
        mgr.resetPassword(Integer.toString(diceRoll), 0);
         mgr.lockNow();
     }
    else {
        Intent intent =
                new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, cn);
        intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                getString(R.string.device_admin_explanation));
        startActivity(intent);
    }
}

this is my resetPasswordAgain method called from my overridden BroadcastReceiver from this class. this method resets the lock screen password to the stored value

public void resetPasswordAgain()
{
    mgr.setPasswordQuality(cn,  DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
    mgr.setPasswordMinimumLength(cn, 0);

    mgr.resetPassword(SetPinDialogFragment.pin, 0);
}

overridden broadcast receiver from this class

 BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent) {
        resetPasswordAgain();
    }
};
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        unregisterReceiver(broadcastReceiver);
    }

I have a basic broadcast receiver in another class also, but this gets overridden by the one shown above

public class AlarmBroadCastReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        context.sendBroadcast(new Intent("CALL_METHOD"));
    }
}

Manifest, just in case you need to look at it

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fbtc141.test3">

<uses-sdk
    android:minSdkVersion="14"
    android:targetSdkVersion="14" />
android:versionCode="1"
android:versionName="1.0">
<uses-feature
    android:name="android.software.device_admin"
    android:required="true" />



<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".ToggleAdmin"
        android:label="@string/app_name">

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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



    </activity>

    <receiver
        android:name=".AdminReceiver"
        android:permission="android.permission.BIND_DEVICE_ADMIN">
        <meta-data
            android:name="android.app.device_admin"
            android:resource="@xml/device_admin" />

        <intent-filter>
            <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
        </intent-filter>
    </receiver>

    <receiver android:name=".AlarmBroadCastReceiver"></receiver>

</application>


Solution

  • When an alarm fires immediately after setting it, it's a good indication that the time set is in the past. In this case, your alarm is based on elapsed real time, which is the time since the last boot. The time you're passing in the set() method - time * 60000 - would therefore most likely be in the past.

    To set a future elapsed real time alarm, you need to add the current elapsed time to the desired interval. For example:

    alMgr.set(AlarmManager.ELAPSED_REALTIME,
        SystemClock.elapsedRealtime() + time * 60000, pendIntent);
    

    I would also mention that, since API 19 (KitKat), the set() method is no longer exact. This might not make much of a difference with a short interval, but if it matters, you can use the setExact() method instead for versions >= 19. Further information can be found in the docs for AlarmManager.