package com.secureline.server;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.KeyguardManager;
import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RecoverySystem;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.server.pm.UserManagerInternal;
import android.service.persistentdata.PersistentDataBlockManager;
import android.provider.Settings;
import android.util.Base64;
import android.util.Slog;
import com.android.secureline.ISecureLineManager;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsInternal;
import com.secureline.server.SecureLineHeartbeatClient;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.cert.Certificate;
import org.json.JSONObject;

public final class SecureLineManagerService extends SystemService {
   private static final String TAG = "SecureLineManager";

   private static final String DEVICE_KEY_ALIAS = "secureline_device_key";

   private static final String SETUP_WIZARD_PACKAGE =
       "app.grapheneos.setupwizard";

   private static final String ALLOWED_PACKAGE =

       "com.secureline.protect";

   private static final String ACTION_DAILY_REEVAL =

       "com.secureline.server.action.DAILY_REEVAL";

   private static final String ACTION_POLL =

       "com.secureline.server.action.POLL";

   private static final String ACTION_HEARTBEAT =

       "com.secureline.server.action.HEARTBEAT";

   private static final String ACTION_SECURELINE_LOCK_WARNING =

       "com.secureline.action.LOCK_WARNING";

   private static final String ACTION_INACTIVITY_WIPE =

       "com.secureline.server.action.INACTIVITY_WIPE";

   private static final String ACTION_BFU_TIMER =

       "com.secureline.server.action.BFU_TIMER";

   private static final String EXTRA_STATE = "state";

   private static final String STATE_FILE =
       "/data/system_de/0/secureline/state";

   private static final String ACTION_SECURELINE_EXPIRY_WARNING =
       "com.secureline.action.EXPIRY_WARNING";

   private static final long POLL_INTERVAL_MS =

       60_000L;

   private static final long HEARTBEAT_INTERVAL_MS =

       300_000L;

   private static final long UNLOCK_STABLE_WINDOW = 2500;

   private int mCurrentState = SecureLineState.BOOTSTRAP;

   private boolean mUserPanicProtectionEnabled = true;

   private volatile boolean mUsbProtectionEnabled = true;

   private boolean mCallUnlockProtectionEnabled = true;

   private boolean mConnectivitySabotageEnabled = true;

   private long mConnectivityTimeoutMs =
           SecureLinePolicy.CONNECTIVITY_15M;

   private SecureLineConnectivityProtection
           mConnectivityProtection;

   private SecureLineUsbProtection mUsbProtection;

   private SecureLineNotificationProtection mNotificationProtection;
   private SecureLineSensorLockdown mSensorLockdown;

   private long mBfuTimerTimeoutMs = SecureLinePolicy.BFU_TIMER_4H;

   private long mInactivityTimeoutMs =

       SecureLinePolicy.DEFAULT_INACTIVITY_TIMEOUT;

   private int mBruteForceLimit = 10;

   private boolean mBfuProtectionEnabled = true;

   private boolean mTamperLockEnabled = true;

   private int mTamperSensitivity = 1;

   private volatile boolean mDebuggingAllowed;

   private long mLastUserUnlockRealtime = SystemClock.elapsedRealtime();

   private long mLastExpiryWarningMs = 0;

   private long mLastLockWarningMs = 0;

   private volatile boolean postUnlockCompleted = false;
   private volatile boolean mVerifiedUnlockPending = false;

   private final Object mStateLock = new Object();

   private PendingIntent mInactivityIntent;

   private PendingIntent mBfuTimerIntent;

   private LockSettingsInternal mLockSettings;

   private SecureLineActivationClient mActivationClient;

   private SecureLineDeviceKeyManager mDeviceKeyManager;

   private SecureLineIdentityStore mIdentityStore;

   private SecureLineLicenseStore mLicenseStore;

   private SecureLinePollClient mPollClient;

   private PendingIntent mHeartbeatIntent;

   private SecureLineHeartbeatClient mHeartbeatClient;

   private SecureLineCommandVerifier mCommandVerifier;

   private SecureLineCommandStore mCommandStore;

   private final SecureLineCommandProcessor mCommandProcessor =

       new SecureLineCommandProcessor(this);

   private SecureLineLicenseVerifier mLicenseVerifier;

   private SecureLineEnforcementEngine mEnforcementEngine;

   private final BinderService mBinderService = new BinderService();

   private AlarmManager mAlarmManager;

   private PendingIntent mDailyReevalIntent;

   private PendingIntent mPollIntent;

   private boolean mAlarmRegistered = false;

   private android.os.HandlerThread mWorkerThread;
   private Handler mWorkerHandler;

   private boolean mHeartbeatRegistered = false;

   public SecureLineManagerService(Context context) {
      super(context);
   }

   @Override

   public void onStart() {
      mActivationClient = new SecureLineActivationClient(getContext());

      mDeviceKeyManager = new SecureLineDeviceKeyManager();

      mIdentityStore = new SecureLineIdentityStore(getContext());

      mLicenseStore = new SecureLineLicenseStore(getContext());

      mCommandStore = new SecureLineCommandStore(getContext());

      mPollClient = new SecureLinePollClient(getContext());

      mHeartbeatClient = new SecureLineHeartbeatClient(getContext());

      mWorkerThread = new android.os.HandlerThread("SecureLineWorker");
      mWorkerThread.start();
      mWorkerHandler = new Handler(mWorkerThread.getLooper());

      ContentResolver cr =
             getContext().getContentResolver();

     /*
      * Restore persisted protection states
      */

     mUsbProtectionEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_usb_protection",
                     1
             ) == 1;

     mUserPanicProtectionEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_panic_protection",
                     1
              ) == 1;

     mCallUnlockProtectionEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_call_unlock_protection",
                     1
             ) == 1;

     mConnectivitySabotageEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_connectivity_protection",
                     1
             ) == 1;

     mBfuProtectionEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_bfu_protection",
                     1
             ) == 1;

     mTamperLockEnabled =
             Settings.Global.getInt(
                     cr,
                     "secureline_tamper_protection",
                     1
             ) == 1;

     /*
     * Restore persistent debugging policy
     *
     * IMPORTANT:
     * Policy survives wipe/factory reset.
     * Admin panel/backend is authoritative.
     */

    if (SecureLinePolicyStore.hasDebuggingPolicy()) {

    mDebuggingAllowed =
            SecureLinePolicyStore
                    .loadDebuggingAllowed();

    Slog.i(
            TAG,
            "Loaded persistent debugging policy="
                    + mDebuggingAllowed
        );

    } else {

    mDebuggingAllowed = false;

    Slog.i(
            TAG,
            "No persistent debugging policy -> debugging blocked"
        );
    }

     /*
      * Restore persisted values
      */

     mConnectivityTimeoutMs =
             Settings.Global.getLong(
                     cr,
                     "secureline_connectivity_timeout",
                     SecureLinePolicy.CONNECTIVITY_15M
             );

     mInactivityTimeoutMs =
             Settings.Global.getLong(
                     cr,
                     "secureline_inactivity_timeout",
                     SecureLinePolicy.DEFAULT_INACTIVITY_TIMEOUT
             );

     mBfuTimerTimeoutMs =
             Settings.Global.getLong(
                     cr,
                     "secureline_bfu_timer",
                     SecureLinePolicy.BFU_TIMER_4H
             );

     mBruteForceLimit =
             Settings.Global.getInt(
                     cr,
                     "secureline_bruteforce_limit",
                     10
             );

     mTamperSensitivity =
             Settings.Global.getInt(
                     cr,
                     "secureline_tamper_sensitivity",
                     1
             );

     Slog.i(TAG,
             "Restored protections:"
             + " usb=" + mUsbProtectionEnabled
             + " panic=" + mUserPanicProtectionEnabled
             + " connectivity=" + mConnectivitySabotageEnabled
             + " bfu=" + mBfuProtectionEnabled
             + " tamper=" + mTamperLockEnabled
             + " inactivity=" + mInactivityTimeoutMs
             + " bfuTimer=" + mBfuTimerTimeoutMs
             + " bruteForce=" + mBruteForceLimit
             + " tamperSensitivity=" + mTamperSensitivity
      );

      applyDeveloperSecurityPolicy();

      mConnectivityProtection =
              new SecureLineConnectivityProtection(

          getContext(),

          new SecureLineConnectivityProtection.Callbacks() {

              @Override
              public boolean isEnabled() {

                  return mConnectivitySabotageEnabled;

              }

              @Override
              public boolean isDeviceLocked() {

                  long lockStart =
                      Settings.Secure.getLong(
                          getContext().getContentResolver(),
                          "secureline_lock_start",
                          0
                      );

                  if (lockStart <= 0) {
                      return false;
                  }

                  KeyguardManager km =
                      (KeyguardManager) getContext().getSystemService(
                          Context.KEYGUARD_SERVICE
                      );

                  return km != null && km.isDeviceLocked();
              }

              @Override
              public long getTimeoutMs() {

                  return mConnectivityTimeoutMs;

              }

              @Override
              public void onViolation(String reason) {

                  KeyguardManager km =
                      (KeyguardManager) getContext().getSystemService(
                          Context.KEYGUARD_SERVICE
                      );

                  long lockStart =
                      Settings.Secure.getLong(
                          getContext().getContentResolver(),
                          "secureline_lock_start",
                          0
                      );

                  if (km == null || !km.isDeviceLocked() || lockStart <= 0) {
                      Slog.w(TAG,
                          "Connectivity reboot ignored - device is unlocked");
                      return;
                  }

                  triggerBfuReboot(
                          "connectivity_sabotage"
                  );

              }

          }

      );

      mUsbProtection = new SecureLineUsbProtection(
          getContext(),
          () -> mUsbProtectionEnabled
      );

      LocalServices.addService(
          SecureLineUsbProtection.class,
          mUsbProtection
      );


      LocalServices.addService(
          SecureLineUsbViolationInternal.class,
          new SecureLineUsbViolationInternal() {
              @Override
              public void onUsbViolation(Violation violation) {
                  // USB violations are already positively classified by the
                  // USB stack. Handle them synchronously so a destructive
                  // response can never wait behind a Handler/worker queue.
                  try {
                      handleUsbViolation(violation);
                  } catch (Throwable t) {
                      Slog.e(TAG, "Direct USB violation handling failed", t);
                      try {
                          triggerImmediateWipe(
                                  "usb_direct_failure_"
                                          + violation.name().toLowerCase());
                      } catch (Throwable wipeFailure) {
                          Slog.wtf(TAG, "Direct USB fail-safe wipe failed", wipeFailure);
                      }
                  }
              }

              @Override
              public void onUsbDisconnected() {
                  synchronized (mUsbViolationLock) {
                      mUsbConnectionGeneration++;
                      mHandledUsbConnectionGeneration = -1;
                  }
              }
          }
      );

      try {
         mCommandVerifier = new SecureLineCommandVerifier();

      } catch (RuntimeException e) {
         Slog.e(TAG,
             "Command verifier unavailable; remote commands will be ignored",
             e);

         mCommandVerifier = null;
      }

      try {
         mLicenseVerifier = new SecureLineLicenseVerifier();

      } catch (RuntimeException e) {
         Slog.e(TAG,
             "License verifier unavailable; activation will continue without "
             + "license verification",
             e);

         mLicenseVerifier = null;
      }

      publishBinderService("secureline", mBinderService);
   }

   @Override
   public void onBootPhase(int phase) {
       if (phase == PHASE_LOCK_SETTINGS_READY) {
              mLockSettings = LocalServices.getService(LockSettingsInternal.class);

           if (mLockSettings == null) {
               Slog.e(TAG, "LockSettingsInternal unavailable at PHASE_LOCK_SETTINGS_READY");
           } else {
               Slog.i(TAG, "LockSettingsInternal acquired");
           }
           return;
       }

       if (phase == PHASE_SYSTEM_SERVICES_READY) {
           Slog.i(TAG, "PHASE_SYSTEM_SERVICES_READY");

	   try {
	        if (mSensorLockdown == null) {
	            mSensorLockdown =
	                   new SecureLineSensorLockdown(getContext());
	       }

	       mSensorLockdown.applyPersistedState();

	       Slog.i(TAG, "Sensor lockdown state applied");

	   } catch (Throwable t) {
	       Slog.e(TAG, "Sensor lockdown init failed", t);
	   }

	   try {
	       if (mNotificationProtection == null) {
	           mNotificationProtection =
	                   new SecureLineNotificationProtection(
	                           getContext(),
	                           reason -> triggerImmediateWipe(reason)
	                   );
	       }

	       LocalServices.addService(
	               SecureLineNotificationWipeInternal.class,
	               (packageName, notification) -> {
	                   if (mNotificationProtection != null) {
	                       mNotificationProtection.onNotificationPosted(
	                               packageName,
	                               notification
	                       );
	                   }
	               }
	       );

	       Slog.i(TAG, "Notification wipe protection registered");

	   } catch (Throwable t) {
	       Slog.e(TAG, "Notification wipe protection init failed", t);
	   }



           if (mConnectivityProtection != null) {
               mConnectivityProtection.start();
           }

           // Apply the persisted USB policy only after all system services,
           // including UsbService, have completed onStart(). This avoids a
           // service-order race during early boot.
           try {
               SecureLineUsbPolicyInternal usbPolicy = LocalServices.getService(
                       SecureLineUsbPolicyInternal.class);
               if (usbPolicy != null) {
                   usbPolicy.onPolicyChanged(false);
               } else {
                   Slog.w(TAG, "SecureLine USB policy service unavailable at boot phase");
               }
           } catch (Throwable t) {
               Slog.e(TAG, "Failed applying SecureLine USB policy at boot", t);
           }

           initialize();

           if (mEnforcementEngine == null) {
               mEnforcementEngine = new SecureLineEnforcementEngine(
                       getContext(),
                       new SecureLineEnforcementEngine.Callbacks() {

                           @Override
                           public boolean isTamperLockEnabled() {
                               return mTamperLockEnabled;
                           }

                           @Override
                           public int getTamperSensitivity() {
                               return mTamperSensitivity;
                           }

                           @Override
                           public int getCurrentState() {
                               return mCurrentState;
                           }

                           @Override
                           public void onUserPresent(int currentState) {
                               // Dummy API hook for future USER_PRESENT policy handling.
                           }

                           @Override
                           public void requestStateReevaluation(String reason) {
                               postToWorker(
                                       "reevaluate_" + reason,
                                       () -> reevaluateStateAndApply(reason)
                               );
                           }

                           @Override
                           public void onDeviceUnlocked() {
                               Slog.d(TAG, "Ignoring EnforcementEngine unlock callback");
                           }

                           @Override
                           public void onDeviceLocked() {
                               notifyDeviceLocked();
                           }

                           @Override
                           public void onSecurityViolation(String reason) {
                               Slog.w(TAG, "Security violation detected: " + reason);

                               if ("tamper_motion".equals(reason)) {
                                   triggerTamperLock();
                                   return;
                               }

                               triggerImmediateWipe(reason);
                           }
                       }
               );

               mEnforcementEngine.start();
           }

           mEnforcementEngine.onStateChanged(mCurrentState);

           setupDailyReevaluation();

           getContext().registerReceiver(
                   mDailyReceiver,
                   new IntentFilter(ACTION_DAILY_REEVAL),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mInactivityReceiver,
                   new IntentFilter(ACTION_INACTIVITY_WIPE),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mBfuTimerReceiver,
                   new IntentFilter(ACTION_BFU_TIMER),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mUserUnlockedReceiver,
                   new IntentFilter(Intent.ACTION_USER_UNLOCKED),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mScreenOffReceiver,
                   new IntentFilter(Intent.ACTION_SCREEN_OFF),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mPollReceiver,
                   new IntentFilter(ACTION_POLL),
                   Context.RECEIVER_NOT_EXPORTED
           );

           getContext().registerReceiver(
                   mHeartbeatReceiver,
                   new IntentFilter(ACTION_HEARTBEAT),
                   Context.RECEIVER_NOT_EXPORTED
           );

           return;
       }
   }

   private void initialize() {

       try {

           mCurrentState =
               loadPersistedState();

           Slog.i(TAG,
               "Loaded persisted state="
               + SecureLineState.toString(mCurrentState));

           mAlarmManager =
               (AlarmManager)
                   getContext().getSystemService(
                       Context.ALARM_SERVICE);

           postUnlockCompleted = false;
           mVerifiedUnlockPending = false;

           mAlarmRegistered = false;
           mHeartbeatRegistered = false;

           UserManager um =
               (UserManager)
                   getContext().getSystemService(
                       Context.USER_SERVICE);

           boolean ceUnlocked =
               um != null && um.isUserUnlocked();

           /*
            * BFU path
            * ABSOLUTELY NO CE / identity reads here
            */
           if (!ceUnlocked) {

               Slog.i(TAG,
                   "BFU boot detected → BFU polling mode");

               applyDeveloperSecurityPolicy();

               resetSchedulers();

               setupPollScheduler();

               postToWorkerDelayed(
                   "initial_bfu_poll",
                   this::performPoll,
                   22000
               );

               return;
           }

           /*
            * AFU path
            * CE reads allowed here
            */

           boolean identityExists =
               mIdentityStore.identityExists();

           boolean deviceKeyExists =
               mDeviceKeyManager.deviceKeyExists();

           if (!identityExists && deviceKeyExists) {

               Slog.w(TAG,
                   "Deleting orphan device key");

               mDeviceKeyManager.deleteDeviceKey();

               deviceKeyExists = false;
           }

           if (!identityExists || !deviceKeyExists) {

               Slog.i(TAG,
                   "SecureLine not activated → BOOTSTRAP");

               mCurrentState =
                   SecureLineState.BOOTSTRAP;

               persistState(mCurrentState);

               applyDeveloperSecurityPolicy();

               enforceBootstrapProvisioning();

               return;
           }

           applyDeveloperSecurityPolicy();

           /*
            * IMPORTANT:
            * do NOT hard-read identity directly during boot init
            * always go through retry pipeline to avoid
            * Tensor / keystore / vold / CE race
            */
           Slog.i(TAG,
               "AFU detected → deferring identity restore to retry pipeline");

           setStateInternal(
               SecureLineState.WAITING_FIRST_UNLOCK
           );

           mVerifiedUnlockPending = true;

           long lockStart =
               Settings.Secure.getLong(
                   getContext().getContentResolver(),
                   "secureline_lock_start",
                   0
               );

           if (lockStart > 0) {

               long now =
                   SystemClock.elapsedRealtime();

               long remaining =
                   (lockStart + mInactivityTimeoutMs) - now;

               if (remaining <= 0) {

                   triggerImmediateWipe("inactivity_timeout");
                   return;
               }

               scheduleInactivityTimer(lockStart);

               if (mBfuProtectionEnabled) {
                   scheduleBfuTimer(lockStart);
               } else {
                   cancelBfuTimer();
               }
           }

           setupPollScheduler();
           setupHeartbeatScheduler();

           postToWorkerDelayed(
               "boot_poll",
               this::performPoll,
               4000
           );

           postToWorkerDelayed(
               "boot_heartbeat",
               this::performHeartbeat,
               6000
           );

           logIdentityStatus();

        } catch (Throwable t) {

            Slog.e(TAG,
                "initialize failure → retrying",
                t);

            postToWorkerDelayed(
                "retry_initialize",
                this::initialize,
                5000
            );
        }
    }

   private void postToWorker(String name, Runnable r) {
      if (mWorkerHandler == null) {
         Slog.e(TAG, "Worker handler unavailable for task: " + name);
         return;
      }

      mWorkerHandler.post(() -> {
         try {
            r.run();
         } catch (Throwable t) {
            Slog.e(TAG, "Worker task failed: " + name, t);
         }
      });
   }

   private void postToWorkerDelayed(String name, Runnable r, long delayMs) {
      if (mWorkerHandler == null) {
         Slog.e(TAG, "Worker handler unavailable for delayed task: " + name);
         return;
      }

      mWorkerHandler.postDelayed(() -> {
         try {
            r.run();
         } catch (Throwable t) {
            Slog.e(TAG, "Delayed worker task failed: " + name, t);
         }
      }, delayMs);
   }

   private void performPoll() {

       try {

           UserManager um =
               (UserManager)
                   getContext().getSystemService(Context.USER_SERVICE);

           boolean unlocked =
               um != null && um.isUserUnlocked();

           /*
            BFU poll ONLY when CE locked
           */

           if (!unlocked) {

               performBfuPoll();

               return;
           }

           /*
            AFU poll
           */

           performFullPoll();

       } catch (Throwable e) {

           Slog.e(TAG,
               "SecureLine poll failed",
               e);
       }
   }

   private void performFullPoll() {
               try {
                  Slog.d(TAG, "Polling server...");

                 if (!mCommandStore.exists())
         {
             Slog.d(TAG,
                 "Command store missing - skipping poll");

             return;
         }

         byte[] cmdBytes =
             mCommandStore.load();

         JSONObject obj =
             new JSONObject(
                 new String(cmdBytes, StandardCharsets.UTF_8)
             );

         String deviceId =
             obj.getString("device_id");

         String nonce = mActivationClient.requestNonce();

         byte[] challenge =

             Base64.decode(

                 nonce,

                 Base64.URL_SAFE | Base64.NO_WRAP

             );

         // SIGN NONCE

         byte[] signature = mDeviceKeyManager.sign(challenge);

         // GET EXISTING ATTESTATION

         SecureLineDeviceKeyManager.AttestationResult att =

             mDeviceKeyManager.getAttestation();

         byte[] attestationBlob =

             BinderService.buildPemAttestation(att.certificateChain);

         SecureLinePollClient.PollResponse resp =

             mPollClient.poll(

                 deviceId,

                 nonce,

                 attestationBlob,

                 signature

             );

         // ==========================

         // LICENSE UPDATE

         // ==========================

         if (resp.licensePayload != null && resp.signature != null) {
            Slog.i(TAG, "License received from server");

            if (mLicenseVerifier == null) {
               Slog.w(TAG,
                   "Skipping license update because verifier is unavailable");

            } else {
               JSONObject license =

                   mLicenseVerifier.verify(

                       resp.licensePayload,

                       resp.signature,

                       deviceId

                   );

               long newCounter = license.getLong("license_counter");

               long currentCounter = mLicenseStore.getLicenseCounter();

               if (newCounter > currentCounter) {
                  mLicenseStore.updateLicense(license);

                  Slog.i(TAG, "License updated counter=" + newCounter);

                  reevaluateStateAndApply("license_update");

                  performHeartbeat();

               } else {
                  Slog.w(TAG,

                      "Ignoring replayed license counter=" + newCounter +

                          " current=" + currentCounter);
               }
            }
         }

         if (resp.commands != null) {
            Slog.i(TAG, "Command received from server");

            if (mCommandVerifier == null) {
               Slog.w(TAG,
                   "Skipping remote commands because verifier is unavailable");

               return;
            }

            for (SecureLinePollClient.Command cmd : resp.commands) {
               JSONObject verified =

                   mCommandVerifier.verify(cmd.payload, cmd.signature);

               mCommandProcessor.process(verified);

               applyDeveloperSecurityPolicy();

            }
         }

      } catch (Exception e) {
         Slog.e(TAG, "SecureLine poll failed", e);
      }
   }

   private void performBfuPoll() {

       try {

           /*
            wait until keystore ready
            avoids Tensor early boot race
           */

           if (!mDeviceKeyManager.deviceKeyExists()) {

               Slog.d(TAG,
                   "BFU poll waiting for device key");

               return;
           }

           if (!mCommandStore.exists()) {

               Slog.d(TAG,
                   "BFU poll skipped - command store not ready");

               return;
           }

           byte[] cmdBytes =
               mCommandStore.load();

           JSONObject obj =
               new JSONObject(
                   new String(
                       cmdBytes,
                       StandardCharsets.UTF_8
                   )
               );

           String deviceId =
               obj.getString("device_id");

           String nonce =
               mActivationClient.requestNonce();

           byte[] challenge =

             Base64.decode(

                 nonce,

                 Base64.URL_SAFE | Base64.NO_WRAP

             );

           byte[] signature;

           try {

               signature =
                   mDeviceKeyManager.sign(challenge);

           } catch (Throwable e) {

               /*
                keystore not ready yet
                normal early boot condition
               */

               Slog.d(TAG,
                   "BFU poll waiting for keystore");

               return;
           }

           SecureLinePollClient.BfuPollResponse resp =
               mPollClient.pollBfu(
                   deviceId,
                   nonce,
                   signature
               );

           if (resp.shouldWipe) {

               Slog.w(TAG,
                   "Remote wipe command received in BFU");

               triggerImmediateWipe(
                   "remote_command_bfu"
               );
           }

       } catch (Throwable e) {

           Slog.w(TAG,
               "BFU poll transient failure",
               e);
       }
   }

   private void setupPollScheduler() {

       if (mAlarmManager == null) {
           Slog.e(TAG, "AlarmManager unavailable");
           return;
       }

       if (mAlarmRegistered) {
           return;
       }

       Intent i = new Intent(ACTION_POLL);
       i.setPackage("android");

       mPollIntent = PendingIntent.getBroadcast(
           getContext(),
           2,
           i,
           PendingIntent.FLAG_UPDATE_CURRENT
               | PendingIntent.FLAG_IMMUTABLE
       );

       scheduleNextPoll(POLL_INTERVAL_MS);

       mAlarmRegistered = true;

       Slog.i(TAG,"Poll scheduler started");
   }

   private void scheduleNextPoll(long delayMs) {
      mAlarmManager.setExactAndAllowWhileIdle(

          AlarmManager.ELAPSED_REALTIME_WAKEUP,

          SystemClock.elapsedRealtime() + delayMs,

          mPollIntent

      );
   }

   private void performHeartbeat() {

      if (!mCommandStore.exists()) {

          Slog.d(TAG,
              "Heartbeat skipped - command store missing");

          return;
      }

      try {
         UserManager um =
             (UserManager) getContext().getSystemService(Context.USER_SERVICE);

         boolean unlocked =
             um != null && um.isUserUnlocked();

         if (!unlocked) {
             Slog.i(TAG, "Skipping heartbeat (BFU)");
             return;
         }

         byte[] cmdBytes =
             mCommandStore.load();

         if (cmdBytes == null)
             return;

         JSONObject identity =
             new JSONObject(
                 new String(cmdBytes, StandardCharsets.UTF_8)
             );

         String deviceId =
             identity.getString("device_id");

         // nonce from backend (anti replay)

         String nonce =

             mActivationClient.requestNonce();

         byte[] challenge =

             Base64.decode(

                 nonce,

                 Base64.URL_SAFE | Base64.NO_WRAP

             );

         // signer nonce with device private key

         byte[] signature =

             mDeviceKeyManager.sign(

                 challenge

             );

         // state from OS enforcement engine

         String state =

             SecureLineState.toString(

                 mCurrentState

             );

         // license status locally

         int daysLeft =

             mLicenseStore

                 .getLicenseDaysLeft();

        // Device Security
        // Read persisted authoritative policy state

        boolean bootloaderLocked =
                isBootloaderLocked();

        boolean debuggingAllowed =
                mDebuggingAllowed;

        boolean bootloaderUnlockAllowed =
                Settings.Global.getInt(
                        getContext().getContentResolver(),
                        "secureline_oem_unlock",
                        0
                ) == 1;

        //  mHeartbeatClient.sendHeartbeat(

        //      deviceId,

        //      state,

        //      daysLeft,

        //      nonce,

        //      signature,

        //      bootloaderLocked,

        //      adbAllowed,

        //      developerOptionsAllowed

        //  );

            mHeartbeatClient.sendHeartbeat(

            deviceId,

            state,

            daysLeft,

            nonce,

            signature,

            bootloaderLocked,

            debuggingAllowed,

            bootloaderUnlockAllowed
        );

         Slog.d(

             TAG,

             "Heartbeat sent state="

                 + state +

                 " daysLeft="

                 + daysLeft

         );

      } catch (Exception e) {
         Slog.e(

             TAG,

             "Heartbeat failed",

             e

         );
      }
   }

   private void resetSchedulers() {

       mAlarmRegistered = false;
       mHeartbeatRegistered = false;

   }

   private void handlePostUserUnlock() {

       if (!mVerifiedUnlockPending) {

           Slog.d(TAG,
               "handlePostUserUnlock ignored - no verified unlock pending");

           return;
       }

       if (postUnlockCompleted) {

           Slog.d(TAG,
               "Post unlock already completed");

           return;
       }

       UserManager um =
           (UserManager)
               getContext().getSystemService(Context.USER_SERVICE);

       if (um == null || !um.isUserUnlocked()) {

           Slog.w(TAG,
               "handlePostUserUnlock called but CE still locked");

           return;
       }

       /*
        FIRST unlock MUST transition immediately
       */

       if (mCurrentState == SecureLineState.WAITING_FIRST_UNLOCK) {

           Slog.i(TAG,
               "FIRST UNLOCK COMPLETE → ACTIVE");

           setStateInternal(
               SecureLineState.ACTIVE
           );
       }

       postToWorker(
           "identity_migration",

           () -> {

               try {

                   mIdentityStore.migrateIdentityAfterFirstUnlock();

                   byte[] identity =
                       mIdentityStore.loadIdentity();

                   if (identity != null) {

                       mLicenseStore.loadFromIdentity(identity);

                       postUnlockCompleted = true;

                       mVerifiedUnlockPending = false;

                       mLastUserUnlockRealtime =
                           SystemClock.elapsedRealtime();

                       Slog.i(TAG,
                           "Identity loaded after unlock");

                       reevaluateStateAndApply(
                           "identity_ready"
                       );

                       setupPollScheduler();

                       setupHeartbeatScheduler();

                       postToWorkerDelayed(
                           "poll_after_unlock",
                           this::performPoll,
                           3000
                       );

                       postToWorkerDelayed(
                           "heartbeat_after_unlock",
                           this::performHeartbeat,
                           5000
                       );

                   } else {

                       Slog.w(TAG,
                           "Identity not ready → retrying");

                       postToWorkerDelayed(
                           "retry_identity_load",
                           this::handlePostUserUnlock,
                           1500
                       );
                   }

               } catch (Throwable e) {

                   Slog.e(TAG,
                       "Identity load failed after unlock",
                       e);

                   /*
                    retry ved transient fejl
                   */

                   postToWorkerDelayed(
                       "retry_identity_exception",
                       this::handlePostUserUnlock,
                       2000
                   );
               }

           }
       );
   }

   private void setupHeartbeatScheduler() {
      if (mHeartbeatRegistered) {
         return;
      }

      Intent i = new Intent(ACTION_HEARTBEAT);

      i.setPackage("android");

      mHeartbeatIntent = PendingIntent.getBroadcast(
          getContext(),
          4,
          i,
          PendingIntent.FLAG_UPDATE_CURRENT
              | PendingIntent.FLAG_IMMUTABLE
      );

      mAlarmManager.setInexactRepeating(
          AlarmManager.ELAPSED_REALTIME_WAKEUP,
          SystemClock.elapsedRealtime() + HEARTBEAT_INTERVAL_MS,
          HEARTBEAT_INTERVAL_MS,
          mHeartbeatIntent
      );

      mHeartbeatRegistered = true;
   }

   private void scheduleBfuTimer(long lockTime) {
      long triggerAt = lockTime + mBfuTimerTimeoutMs;

      Intent i = new Intent(ACTION_BFU_TIMER);

      i.setPackage("android");

      mBfuTimerIntent = PendingIntent.getBroadcast(

          getContext(),

          3,

          i,

          PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE

      );

      mAlarmManager.setExactAndAllowWhileIdle(

          AlarmManager.ELAPSED_REALTIME_WAKEUP,

          triggerAt,

          mBfuTimerIntent

      );
   }

   private void setupDailyReevaluation() {
      if (mAlarmManager == null)
         return;

      Intent intent = new Intent(ACTION_DAILY_REEVAL);

      intent.setPackage("android");

      mDailyReevalIntent = PendingIntent.getBroadcast(

          getContext(),

          0,

          intent,

          PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT

      );

      long interval = 24L * 60 * 60 * 1000;

      mAlarmManager.setInexactRepeating(

          AlarmManager.ELAPSED_REALTIME_WAKEUP,

          SystemClock.elapsedRealtime() + interval,

          interval,

          mDailyReevalIntent

      );
   }

   private final BroadcastReceiver mDailyReceiver =
       new BroadcastReceiver() {

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

               if (!ACTION_DAILY_REEVAL.equals(intent.getAction())) {
                   return;
               }

               Slog.i(TAG, "Daily state reevaluation");

               postToWorker("daily_reeval", () -> reevaluateStateAndApply("daily_check"));
           }
       };

   private final BroadcastReceiver mPollReceiver =
       new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
             if (!ACTION_POLL.equals(intent.getAction())) {
                return;
             }

             postToWorker("poll", () -> {
                performPoll();
                scheduleNextPoll(POLL_INTERVAL_MS);
             });
          }
       };

   private final BroadcastReceiver mHeartbeatReceiver =
       new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
             if (!ACTION_HEARTBEAT.equals(intent.getAction())) {
                return;
             }

             postToWorker("heartbeat", () -> performHeartbeat());
          }
       };

   private final BroadcastReceiver mUserUnlockedReceiver =
       new BroadcastReceiver() {

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

               if (!Intent.ACTION_USER_UNLOCKED.equals(intent.getAction()))
                   return;

               Slog.i(TAG,"USER_UNLOCKED broadcast");

               if (!mVerifiedUnlockPending) {

                   Slog.d(TAG,
                       "USER_UNLOCKED ignored - waiting for verified credential");

                   return;
               }

               postToWorker(
                   "post_unlock",
                   () -> handlePostUserUnlock()
               );
           }
       };

   private final BroadcastReceiver mScreenOffReceiver =
       new BroadcastReceiver() {

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

               if (!Intent.ACTION_SCREEN_OFF.equals(intent.getAction()))
                   return;

               final Handler h =
                   new Handler(Looper.getMainLooper());

               final long start =
                   SystemClock.elapsedRealtime();

               final long timeout = 10000;

               final long stableDuration = 600;

               final long[] lockedSince = {0};

               Runnable check = new Runnable() {

                   @Override
                   public void run() {

                       KeyguardManager km =
                           (KeyguardManager)
                               getContext().getSystemService(
                                   Context.KEYGUARD_SERVICE
                               );

                       if (km != null
                            && km.isDeviceLocked()
                            && km.isDeviceSecure()) {

                           long now =
                               SystemClock.elapsedRealtime();

                           if (lockedSince[0] == 0) {

                               lockedSince[0] = now;

                           } else if (now - lockedSince[0] >= stableDuration) {

                               notifyDeviceLocked();

                               return;
                           }

                       } else {

                           lockedSince[0] = 0;
                       }

                       if (SystemClock.elapsedRealtime() - start < timeout) {

                           h.postDelayed(this, 200);

                       } else {

                           Slog.i(TAG,
                               "SCREEN_OFF but never stabilized as locked");
                       }
                   }
               };

               h.postDelayed(check, 500);
           }
       };

   private void scheduleInactivityTimer(long lockTime) {
      long triggerAt = lockTime + mInactivityTimeoutMs;

      Intent i = new Intent(ACTION_INACTIVITY_WIPE);

      i.setPackage("android");

      mInactivityIntent = PendingIntent.getBroadcast(

          getContext(),

          1,

          i,

          PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE

      );

      mAlarmManager.setExactAndAllowWhileIdle(

          AlarmManager.ELAPSED_REALTIME_WAKEUP,

          triggerAt,

          mInactivityIntent

      );
   }

   private void cancelInactivityTimer() {
      if (mInactivityIntent == null)
         return;

      mAlarmManager.cancel(mInactivityIntent);
   }

   private void cancelBfuTimer() {
      if (mBfuTimerIntent == null)
         return;

      mAlarmManager.cancel(mBfuTimerIntent);
   }

   private final BroadcastReceiver mInactivityReceiver =

       new BroadcastReceiver() {
          @Override

          public void onReceive(Context context, Intent intent) {
             if (!ACTION_INACTIVITY_WIPE.equals(intent.getAction()))

                return;

             Slog.w(TAG, "Inactivity timeout reached → wiping device");

             triggerImmediateWipe("inactivity_timeout");
          }
       };

   private final BroadcastReceiver mBfuTimerReceiver =

       new BroadcastReceiver() {
          @Override

          public void onReceive(Context context, Intent intent) {
             if (!ACTION_BFU_TIMER.equals(intent.getAction()))

                return;

             if (!mBfuProtectionEnabled) {
                Slog.i(TAG,
                    "BFU timer ignored - protection disabled");
                return;
             }

             long lockStart =
                 Settings.Secure.getLong(
                     getContext().getContentResolver(),
                     "secureline_lock_start",
                     0
                 );

             KeyguardManager km =
                 (KeyguardManager) getContext().getSystemService(
                     Context.KEYGUARD_SERVICE
                 );

             if (lockStart <= 0 || km == null || !km.isDeviceLocked()) {
                Slog.w(TAG,
                    "BFU timer ignored - device is currently unlocked");
                cancelBfuTimer();
                return;
             }

             Slog.w(TAG, "BFU timer expired → rebooting to BFU");

             try {
                PowerManager pm =

                    (PowerManager) getContext().getSystemService(
                        Context.POWER_SERVICE);

                if (pm != null) {
                   pm.reboot("secureline_bfu_timer");
                }

             } catch (Exception e) {
                Slog.e(TAG, "BFU reboot failed", e);
             }
          }
       };

   public void notifyDeviceLocked() {

       if (mCurrentState == SecureLineState.WAITING_FIRST_UNLOCK) {
           Slog.i(TAG, "Ignoring lock until first real unlock");
           return;
       }

       KeyguardManager km =
           (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);

       if (km == null || !km.isDeviceSecure()) {
           Slog.d(TAG, "notifyDeviceLocked ignored - not secure");
           return;
       }

       /*
        * A screen-on / keyguard-visible event is NOT an unlock.
        * Only start a lock session when Android confirms that the device is
        * actually credential-locked. Merely waking the display must not
        * create or reset SecureLine lock/reboot state.
        */
       if (!km.isDeviceLocked()) {
           Slog.d(TAG, "notifyDeviceLocked ignored - device is not actually locked");
           return;
       }

       final long now = SystemClock.elapsedRealtime();

       if ((now - mLastUserUnlockRealtime) < UNLOCK_STABLE_WINDOW) {
           Slog.d(TAG, "Ignoring lock bounce after unlock");
           return;
       }

       postToWorker("start_lock_session", () -> {

           /*
            * Re-check at execution time as well. The device may have been
            * successfully unlocked after this work item was queued.
            */
           KeyguardManager workerKm =
               (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);

           if (workerKm == null || !workerKm.isDeviceLocked()) {
               Slog.d(TAG,
                   "Lock session start ignored - device unlocked before worker execution");
               return;
           }

           long existing = Settings.Secure.getLong(
               getContext().getContentResolver(),
               "secureline_lock_start",
               0
           );

           if (existing > 0) {
               Slog.d(TAG, "Lock session already active");
               return;
           }

           long lockStart = SystemClock.elapsedRealtime();

           Settings.Secure.putLong(
               getContext().getContentResolver(),
               "secureline_lock_start",
               lockStart
           );

           scheduleInactivityTimer(lockStart);

           if (mBfuProtectionEnabled) {
               scheduleBfuTimer(lockStart);
           } else {
               cancelBfuTimer();
           }

           Slog.i(TAG, "LOCK SESSION STARTED at=" + lockStart);
           Slog.i(TAG, "Inactivity trigger=" + (lockStart + mInactivityTimeoutMs));

           if (mBfuProtectionEnabled) {
               Slog.i(TAG, "BFU trigger=" + (lockStart + mBfuTimerTimeoutMs));
           }

       });
   }

   public void notifyDeviceUnlocked() {

       Slog.d(TAG,
           "notifyDeviceUnlocked ignored - handled via notifyVerifiedUnlock");
   }

   public void notifyVerifiedUnlock() {

       Slog.i(TAG, "Verified unlock → clearing state");

       final SecureLineUsbEnforcementInternal usbEnforcement =
               LocalServices.getService(SecureLineUsbEnforcementInternal.class);
       if (usbEnforcement != null) {
           usbEnforcement.setUsbDataBlocked(false);
       }


       synchronized (mUsbViolationLock) {
           mUsbConnectionGeneration++;
           mHandledUsbConnectionGeneration = -1;
       }

       mVerifiedUnlockPending = true;

       mLastUserUnlockRealtime =
           SystemClock.elapsedRealtime();

       /*
        * Trigger license warnings
        * on verified unlock
        */

        handleLicenseWarningOnUnlock();

       Slog.i(TAG, "Cancelling timers due to verified unlock");

       cancelInactivityTimer();
       cancelBfuTimer();

       Settings.Secure.putLong(
           getContext().getContentResolver(),
           "secureline_lock_start",
           0
       );

       postToWorker(
           "force_post_unlock",
           this::handlePostUserUnlock
       );
   }

   private final Object mUsbViolationLock = new Object();
   private long mUsbConnectionGeneration;
   private long mHandledUsbConnectionGeneration = -1;

   private void handleUsbViolation(
           SecureLineUsbViolationInternal.Violation violation) {
       synchronized (mUsbViolationLock) {
           if (mHandledUsbConnectionGeneration == mUsbConnectionGeneration) {
               Slog.w(TAG, "Duplicate USB violation ignored: " + violation);
               return;
           }

           if (!mUsbProtectionEnabled) {
               Slog.w(TAG, "USB violation ignored because protection is disabled");
               return;
           }

           UserManager um = getContext().getSystemService(UserManager.class);
           KeyguardManager km = getContext().getSystemService(KeyguardManager.class);
           boolean locked = um == null || !um.isUserUnlocked()
                   || km == null || km.isDeviceLocked();
           if (!locked) {
               Slog.w(TAG, "USB violation ignored because device is unlocked: " + violation);
               return;
           }

           final SecureLineUsbEnforcementInternal usbEnforcement =
                   LocalServices.getService(SecureLineUsbEnforcementInternal.class);
           final boolean usbBlocked = usbEnforcement != null
                   && usbEnforcement.blockUsbData();
           if (!usbBlocked) {
               Slog.e(TAG, "Emergency USB data block request failed before wipe");
           }

           mHandledUsbConnectionGeneration = mUsbConnectionGeneration;
           triggerImmediateWipe("usb_" + violation.name().toLowerCase());
       }
   }

   public void triggerImmediateWipe(String reason) {

           Slog.e(TAG,
               "SECURE WIPE REQUESTED: " + reason);

           SecureLineWipe.trigger(
               getContext(),
               reason
           );
       }

   public void triggerBfuReboot(String reason) {
      Slog.w(

          TAG,

          "Triggering BFU reboot due to: " + reason

      );

      try {
         PowerManager pm =

             (PowerManager)

                 getContext()
                     .getSystemService(

                         Context.POWER_SERVICE

                     );

         if (pm != null) {
            pm.reboot(

                "secureline_bfu_" + reason

            );

         } else {
            Slog.e(

                TAG,

                "PowerManager null during BFU reboot"

            );
         }

      } catch (Throwable t) {
         Slog.e(

             TAG,

             "BFU reboot failed",

             t

         );
      }
   }

   public void triggerTamperLock() {

       if (!mTamperLockEnabled)
           return;

       android.app.KeyguardManager km =
           (android.app.KeyguardManager)
               getContext().getSystemService(
                   Context.KEYGUARD_SERVICE
               );

       if (km != null && km.isDeviceLocked()) {

           Slog.i(TAG,
               "Tamper ignored - device already locked");

           return;
       }

       Slog.w(TAG,
           "Tamper detected → locking device");

       try {

           PowerManager pm =
               (PowerManager)
                   getContext().getSystemService(
                       Context.POWER_SERVICE
                   );

           if (pm != null) {

               pm.goToSleep(
                   SystemClock.uptimeMillis(),
                   PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN,
                   0
               );

               notifyDeviceLocked();

               Slog.i(TAG,
                   "Tamper lock executed");
           }

       } catch (Throwable t) {

           Slog.e(TAG,
               "Tamper lock failed",
               t);
       }
   }

   private void applyDeveloperSecurityPolicy() {

    final ContentResolver cr =
            getContext().getContentResolver();

    final long token =
            Binder.clearCallingIdentity();

    try {

        /*
         * Developer options
         */

        Settings.Global.putInt(
                cr,
                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
                mDebuggingAllowed ? 1 : 0
        );

        /*
         * ADB
         */

        Settings.Global.putInt(
                cr,
                Settings.Global.ADB_ENABLED,
                mDebuggingAllowed ? 1 : 0
        );

        /*
         * HARD debugging restriction
         *
         * When blocked:
         * - user cannot enable developer options
         * - build number unlock blocked
         * - USB debugging blocked
         * - debugging ecosystem locked
         *
         * Only SecureLine policy may change this.
         */

        UserManagerInternal umi =
                LocalServices.getService(
                        UserManagerInternal.class
                );

        if (umi != null) {

            umi.setUserRestriction(
                    UserHandle.USER_SYSTEM,
                    UserManager.DISALLOW_DEBUGGING_FEATURES,
                    !mDebuggingAllowed
            );

        } else {

            Slog.w(
                    TAG,
                    "UserManagerInternal unavailable"
            );
        }

    } finally {

        Binder.restoreCallingIdentity(token);
    }

    Slog.i(
            TAG,
            "Developer security policy applied"
                    + " debugging=" + mDebuggingAllowed
       );
   }

   private void reevaluateStateAndApply(String reason) {

       synchronized (mStateLock) {

           try {

               final long lockStart =
                   Settings.Secure.getLong(
                       getContext().getContentResolver(),
                       "secureline_lock_start",
                       0
                   );

               final long lockedFor =
                   lockStart > 0
                       ? SystemClock.elapsedRealtime() - lockStart
                       : 0;

               final UserManager um =
                   (UserManager) getContext()
                       .getSystemService(Context.USER_SERVICE);

               final boolean unlocked =
                   um != null && um.isUserUnlocked();

               // ==============================
               // INACTIVITY PROTECTION
               // ==============================

               if (lockStart > 0 && lockedFor > mInactivityTimeoutMs) {

                   Slog.w(TAG,
                       "Inactivity timeout exceeded during active lock session");

                   triggerImmediateWipe("inactivity_timeout");

                   return;
               }

               // ==============================
               // BFU TIMER PROTECTION
               // ==============================

               if (!unlocked
                       && mBfuProtectionEnabled
                       && lockedFor > mBfuTimerTimeoutMs) {

                   Slog.w(TAG,
                       "BFU timer expired → rebooting to BFU");

                   triggerBfuReboot("bfu_timer");

                   return;
               }

               // ==============================
               // BFU MODE
               // ==============================

               if (!unlocked) {

                   Slog.i(TAG,
                       "BFU detected → preserving state="
                       + SecureLineState.toString(mCurrentState));

                   return;
               }

               // ==============================
               // AFU STATE CALCULATION
               // ==============================

               byte[] identityBytes;

               try {

                   identityBytes =
                       mIdentityStore.loadIdentity();

               } catch (Throwable e) {

                   Throwable cause = e;
                   boolean retryable = false;

                   while (cause != null) {

                       if (cause instanceof java.security.InvalidKeyException
                               || cause instanceof android.security.KeyStoreException) {

                           retryable = true;
                           break;
                       }

                       cause = cause.getCause();
                   }

                   if (retryable) {

                       Slog.i(TAG,
                           "Keystore not ready yet");

                       if (!mVerifiedUnlockPending) {

                           Slog.w(TAG,
                               "Keystore unavailable outside unlock flow → skip retry");

                           return;
                       }

                       postToWorkerDelayed(
                           "retry_reeval_keystore",
                           () -> reevaluateStateAndApply("keystore_retry"),
                           2000
                       );

                       return;
                   }

                   throw e;
               }

               if (identityBytes == null) {

                   Slog.i(TAG,
                       "Identity not ready yet");

                   if (!mVerifiedUnlockPending) {

                       Slog.w(TAG,
                           "Identity null outside unlock flow → skip retry");

                       return;
                   }

                   postToWorkerDelayed(
                       "retry_reeval_null_identity",
                       () -> reevaluateStateAndApply("identity_retry"),
                       2000
                   );

                   return;
               }

               mLicenseStore.loadFromIdentity(identityBytes);

               int newState = calculateState();

               if (!isBootloaderLocked()) {

                   Slog.w(TAG,
                       "Bootloader unlocked detected");
               }

               setStateInternal(newState);

           } catch (Throwable t) {

               Slog.e(TAG,
                   "Reevaluation failed → retrying",
                   t);

               /*
                identity / keystore / json failures
                can occur during early boot or unlock race
               */

               postToWorkerDelayed(
                   "retry_reeval_exception",
                   () -> reevaluateStateAndApply("retry_exception"),
                   5000
              );
          }
      }
  }

  private void setStateInternal(int newState) {

       /*
        BOOTSTRAP må aldrig ske efter activation
       */

       if (mCurrentState != SecureLineState.BOOTSTRAP
               && newState == SecureLineState.BOOTSTRAP) {

           Slog.w(TAG,
               "Ignoring invalid regression to BOOTSTRAP");

           return;
       }

       if (newState == mCurrentState)
           return;

       Slog.i(
           TAG,
           "STATE CHANGE "
               + SecureLineState.toString(mCurrentState)
               + " → "
               + SecureLineState.toString(newState)
       );

       mCurrentState = newState;

        /*
         * Clear SecureLine warnings
         * when device becomes ACTIVE again
         */

        if (newState == SecureLineState.ACTIVE) {

            Intent i =
                    new Intent(
                            "com.secureline.action.ACTIVE"
                    );

            i.setPackage("com.android.systemui");

            getContext().sendBroadcastAsUser(
                    i,
                    UserHandle.SYSTEM,
                    android.Manifest.permission.STATUS_BAR
            );
        }

       persistState(newState);

       if (mEnforcementEngine != null) {
           mEnforcementEngine.onStateChanged(newState);
       }

       applyDeveloperSecurityPolicy();

        if (newState != SecureLineState.BOOTSTRAP) {

            setupPollScheduler();
            setupHeartbeatScheduler();

            postToWorkerDelayed(
                "initial_poll_after_state_change",
                this::performPoll,
                4000
            );

            postToWorkerDelayed(
                "initial_heartbeat_after_state_change",
                () -> performHeartbeat(),
                6000
            );
        }
   }

   public void setDebuggingAllowed(boolean allowed) {

    enforceSystemCaller();

    mDebuggingAllowed = allowed;

    final long token =
            Binder.clearCallingIdentity();

    try {

        /*
         * Runtime cache
         */

        Settings.Global.putInt(
                getContext().getContentResolver(),
                "secureline_debugging_allowed",
                allowed ? 1 : 0
        );

        /*
         * Persistent policy
         */

        SecureLinePolicyStore.saveDebuggingAllowed(
                allowed
        );

    } finally {

        Binder.restoreCallingIdentity(token);
    }

    applyDeveloperSecurityPolicy();

    Slog.i(
            TAG,
            "Debugging allowed=" + allowed
       );
   }

   public void setBootloaderUnlockAllowed(boolean allowed) {
    enforceSystemCaller();

    final long token =
            Binder.clearCallingIdentity();

    try {
        Settings.Global.putInt(
                getContext().getContentResolver(),
                "secureline_oem_unlock",
                allowed ? 1 : 0
        );
    } finally {
        Binder.restoreCallingIdentity(token);
    }

    try {
        android.service.oemlock.IOemLockService oem =
                android.service.oemlock.IOemLockService.Stub.asInterface(
                        android.os.ServiceManager.getService(
                                Context.OEM_LOCK_SERVICE
                        )
                );

        if (oem != null) {
            oem.isOemUnlockAllowed();
        } else {
            Slog.w(TAG, "OemLockService unavailable");
        }
    } catch (Throwable t) {
        Slog.e(TAG, "Failed syncing OEM lock state", t);
    }

    Slog.i(TAG, "OEM unlock allowed=" + allowed);
   }

   private int calculateState() {
      return SecureLinePolicy.calculateState(mLicenseStore);
   }

   private void persistState(int state) {

       try {

           java.nio.file.Path dir =
                   java.nio.file.Paths.get("/data/system_de/0/secureline");

           if (!java.nio.file.Files.exists(dir)) {

               java.nio.file.Files.createDirectories(dir);

               java.io.File dirFile = dir.toFile();

               dirFile.setReadable(false, false);
               dirFile.setWritable(true, true);
               dirFile.setExecutable(true, true);
           }

           java.nio.file.Path tmp =
               java.nio.file.Paths.get(
                   STATE_FILE + ".tmp"
               );

           java.nio.file.Files.write(

               tmp,

               String.valueOf(state).getBytes()

           );

           java.nio.file.Files.move(

               tmp,

               java.nio.file.Paths.get(STATE_FILE),

               java.nio.file.StandardCopyOption.REPLACE_EXISTING,

               java.nio.file.StandardCopyOption.ATOMIC_MOVE

           );

       } catch (Exception e) {

           Slog.e(TAG,
               "Failed persisting state",
               e
           );
       }
   }

   private int loadPersistedState() {

       try {

           java.nio.file.Path p =
               java.nio.file.Paths.get(STATE_FILE);

           if (!java.nio.file.Files.exists(p)) {

               return SecureLineState.BOOTSTRAP;
           }

           String s =
               new String(java.nio.file.Files.readAllBytes(p));

           return Integer.parseInt(s.trim());

       } catch (Exception e) {

           Slog.w(TAG,"State load failed → BOOTSTRAP", e);

           return SecureLineState.BOOTSTRAP;
       }
   }

   private void logIdentityStatus() {

       boolean identityExists = mIdentityStore.identityExists();
       boolean deviceKeyExists = mDeviceKeyManager.deviceKeyExists();

       String verifiedBoot = android.os.SystemProperties.get(
               "ro.boot.verifiedbootstate",
               "unknown"
       );

       String bootloaderLocked = android.os.SystemProperties.get(
               "ro.boot.flash.locked",
               "unknown"
       );

       Slog.i(TAG,
           "IDENTITY STATUS "
           + " identityExists=" + identityExists
           + " deviceKeyExists=" + deviceKeyExists
           + " verifiedBoot=" + verifiedBoot
           + " flashLocked=" + bootloaderLocked
       );

       if (deviceKeyExists) {

           String fingerprint =
               mDeviceKeyManager.getPublicKeyFingerprint();

           Slog.i(TAG,
               "DEVICE KEY FP=" + fingerprint
           );
       }
   }

   private boolean isBootloaderLocked() {
      String state = android.os.SystemProperties.get(

          "ro.boot.flash.locked",

          "0"

      );

      return "1".equals(state);
   }

   private void enforceBootstrapProvisioning() {
      try {
         Settings.Global.putInt(

             getContext().getContentResolver(),

             Settings.Global.DEVICE_PROVISIONED,

             0

         );

         Settings.Secure.putIntForUser(

             getContext().getContentResolver(),

             Settings.Secure.USER_SETUP_COMPLETE,

             0,

             UserHandle.USER_SYSTEM

         );

      } catch (Exception e) {
         Slog.e(TAG, "Failed enforcing bootstrap provisioning", e);
      }
   }

   private void handleLicenseWarningOnUnlock() {

       int daysLeft =
               mLicenseStore.getLicenseDaysLeft();

       long now =
               SystemClock.elapsedRealtime();

       /*
        * PRE-EXPIRY WARNINGS
        */

       if (mCurrentState == SecureLineState.ACTIVE) {

           if (daysLeft <= 7 && daysLeft > 0) {

               if ((now - mLastExpiryWarningMs)
                       > (2L * 60L * 60L * 1000L)) {

                   mLastExpiryWarningMs = now;

                   sendExpiryWarningBroadcast(daysLeft);
               }
           }

           return;
       }

       /*
        * EXPIRED / LOCK WARNINGS
        */

       if (mCurrentState == SecureLineState.MID_LOCK
               || mCurrentState == SecureLineState.HARD_LOCK
               || daysLeft <= 0) {

           if ((now - mLastLockWarningMs)
                   > (15L * 60L * 1000L)) {

               mLastLockWarningMs = now;

               sendLockWarningBroadcast(mCurrentState);
           }
       }
   }

   private void sendLockWarningBroadcast(int state) {
      if (state != SecureLineState.MID_LOCK &&

          state != SecureLineState.HARD_LOCK) {
         return;
      }

      Intent i = new Intent(ACTION_SECURELINE_LOCK_WARNING);

      i.setPackage("com.android.systemui");

      i.putExtra(
              EXTRA_STATE,
              SecureLineState.toString(state)
      );

      Slog.i(
              TAG,
              "Sending lock warning state="
                      + SecureLineState.toString(state)
      );

      getContext().sendBroadcastAsUser(

          i,

          UserHandle.SYSTEM,

          android.Manifest.permission.STATUS_BAR

      );
   }

   private void sendExpiryWarningBroadcast(int daysLeft) {

       if (daysLeft < 0)
           return;

       Intent i =
           new Intent(ACTION_SECURELINE_EXPIRY_WARNING);

       i.setPackage("com.android.systemui");

       i.putExtra("days_left", daysLeft);

       Slog.i(
               TAG,
               "Sending expiry warning daysLeft="
                       + daysLeft
       );

       getContext().sendBroadcastAsUser(
           i,
           UserHandle.SYSTEM,
           android.Manifest.permission.STATUS_BAR
       );
   }

   private void enforceSystemCaller() {
         final int uid = Binder.getCallingUid();

         /*

          * allow internal system_server calls

          */

         if (uid == Process.SYSTEM_UID) {
            return;
         }

         final PackageManager pm =

             getContext().getPackageManager();

         final String[] packages =

             pm.getPackagesForUid(uid);

         if (packages != null) {
            for (String pkg : packages) {
               if (ALLOWED_PACKAGE.equals(pkg)) {
                  /*

                   * verify platform signature

                   */

                  if (pm.checkSignatures(

                          pkg,

                          "android"

                          )
                      == PackageManager.SIGNATURE_MATCH) {
                     return;
                  }
               }
            }
         }

         throw new SecurityException(

             "SecureLine: unauthorized caller uid=" + uid

         );
      }

   private final class BinderService extends ISecureLineManager.Stub {
      @Override

      public int getState() {
         return mCurrentState;
      }

      @Override

      public boolean hasIdentity() {
         return mIdentityStore.identityExists();
      }

      @Override

      public String getSupportId() {
         try {
            byte[] cmd = mCommandStore.load();

            if (cmd == null)
               return "";

            JSONObject obj =

                new JSONObject(new String(cmd, StandardCharsets.UTF_8));

            return obj.optString("support_id", "");

         } catch (Exception e) {
            Slog.e(TAG, "support_id read failed", e);

            return "";
         }
      }

      @Override

      public String getWipeToken() {
         try {

            byte[] cmd = mCommandStore.load();

            if (cmd == null)
               return "";

            JSONObject obj =

                new JSONObject(new String(cmd, StandardCharsets.UTF_8));

            return obj.optString("wipe_token", "");

         } catch (Exception e) {
            Slog.e(TAG, "wipe_token read failed", e);

            return "";
         }
      }

      @Override

      public String getLicensePlan() {

          try {

              return mLicenseStore.getLicensePlan();

          } catch (Exception e) {

              Slog.e(TAG,
                  "Failed reading license plan",
                  e
              );

              return "";
          }
      }

      @Override

      public boolean isLicenseActive() {
         try {
            return mLicenseStore.isLicenseActive();

         } catch (Exception e) {
            Slog.e(TAG, "License status error", e);

            return false;
         }
      }

      @Override

      public int getLicenseDaysLeft() {
         try {
            return mLicenseStore.getLicenseDaysLeft();

         } catch (Exception e) {
            Slog.e(TAG, "Failed reading license days left", e);

            return -1;
         }
      }

      @Override

      public boolean isPanicProtectionEnabled() {
         return mUserPanicProtectionEnabled;
      }

      @Override

      public void setPanicProtectionEnabled(boolean enabled) {
         enforceSystemCaller();

         mUserPanicProtectionEnabled = enabled;

            final long token = Binder.clearCallingIdentity();

            try {

                Settings.Global.putInt(
                        getContext().getContentResolver(),
                        "secureline_panic_protection",
                        enabled ? 1 : 0
                );

            } finally {

                Binder.restoreCallingIdentity(token);
            }

         Slog.i(TAG, "Panic protection changed: " + enabled);
      }

      @Override

      public boolean isUsbProtectionEnabled() {
         return mUsbProtectionEnabled;
      }

      @Override

      public void setUsbProtectionEnabled(boolean enabled) {
          enforceSystemCaller();

          if (mUsbProtectionEnabled == enabled) {
               return;
          }

          mUsbProtectionEnabled = enabled;

          final long token = Binder.clearCallingIdentity();

          try {

              Settings.Global.putInt(
                      getContext().getContentResolver(),
                      "secureline_usb_protection",
                      enabled ? 1 : 0
              );

          } finally {

              Binder.restoreCallingIdentity(token);
          }

          // 🔥 SIGNAL TIL USB SUBSYSTEM
          try {
              SecureLineUsbPolicyInternal usbPolicy =
                  LocalServices.getService(SecureLineUsbPolicyInternal.class);

              if (usbPolicy != null) {
                  usbPolicy.onPolicyChanged(enabled);
              } else {
                  Slog.w(TAG, "SecureLineUsbPolicyInternal not available");
              }

          } catch (Throwable t) {
              Slog.e(TAG, "USB policy notify failed", t);
          }

          if (!enabled) {
              SecureLineUsbEnforcementInternal usbEnforcement =
                      LocalServices.getService(SecureLineUsbEnforcementInternal.class);
              if (usbEnforcement != null) {
                  usbEnforcement.setUsbDataBlocked(false);
              }
          }

          Slog.i(TAG, "USB Protection changed: " + enabled);
      }

      @Override
      public boolean isCallUnlockProtectionEnabled() {
          return mCallUnlockProtectionEnabled;
      }

      @Override
      public void setCallUnlockProtectionEnabled(boolean enabled) {
          enforceSystemCaller();

          mCallUnlockProtectionEnabled = enabled;

          final long token = Binder.clearCallingIdentity();

          try {
              Settings.Global.putInt(
                      getContext().getContentResolver(),
                      "secureline_call_unlock_protection",
                      enabled ? 1 : 0
              );
          } finally {
              Binder.restoreCallingIdentity(token);
          }

          Slog.i(TAG, "Call Unlock Protection changed: " + enabled);
      }

      @Override
      public boolean isMessageWipeProtectionEnabled() {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              return SecureLineNotificationWipeStore
                      .isEnabled(getContext());
          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public void setMessageWipeProtectionEnabled(boolean enabled) {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              SecureLineNotificationWipeStore
                      .setEnabled(getContext(), enabled);

              Slog.i(
                      TAG,
                      "Message Wipe Protection changed: " + enabled
              );
          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public void setMessageWipeSecret(String secret) {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              SecureLineNotificationWipeStore
                      .setSecret(getContext(), secret);

              Slog.i(TAG, "Message wipe secret updated");
          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public boolean isCameraLockdownEnabled() {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              return mSensorLockdown != null
                      && mSensorLockdown.isCameraLockdownEnabled();
          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public void setCameraLockdownEnabled(boolean enabled) {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              if (mSensorLockdown == null) {
                  mSensorLockdown =
                          new SecureLineSensorLockdown(getContext());
              }

              mSensorLockdown.setCameraLockdown(enabled);

              Slog.i(
                      TAG,
                      "Camera Lockdown changed: " + enabled
              );

          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public boolean isMicrophoneLockdownEnabled() {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              return mSensorLockdown != null
                      && mSensorLockdown.isMicrophoneLockdownEnabled();
          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public void setMicrophoneLockdownEnabled(boolean enabled) {
          enforceSystemCaller();

          final long token = Binder.clearCallingIdentity();

          try {
              if (mSensorLockdown == null) {
                  mSensorLockdown =
                          new SecureLineSensorLockdown(getContext());
              }

              mSensorLockdown.setMicrophoneLockdown(enabled);

              Slog.i(
                      TAG,
                      "Microphone Lockdown changed: " + enabled
              );

          } finally {
              Binder.restoreCallingIdentity(token);
          }
      }

      @Override
      public boolean isConnectivitySabotageProtectionEnabled() {

          return mConnectivitySabotageEnabled;

      }

      @Override
      public void setConnectivitySabotageProtectionEnabled(
              boolean enabled
      ) {

          enforceSystemCaller();

          mConnectivitySabotageEnabled = enabled;

          final long token = Binder.clearCallingIdentity();

          try {

              Settings.Global.putInt(
                      getContext().getContentResolver(),
                      "secureline_connectivity_protection",
                      enabled ? 1 : 0
              );

          } finally {

              Binder.restoreCallingIdentity(token);
          }

              Slog.i(TAG, "Connectivity Sabotage Protection changed: " + enabled);
      }

      @Override
      public long getConnectivitySabotageTimeoutMs() {

          return mConnectivityTimeoutMs;

      }

      @Override
      public void setConnectivitySabotageTimeoutMs(
              long timeoutMs
      ) {

          enforceSystemCaller();

          if (timeoutMs !=
                  SecureLinePolicy.CONNECTIVITY_15M &&
              timeoutMs !=
                  SecureLinePolicy.CONNECTIVITY_30M) {

              throw new IllegalArgumentException(
                      "Invalid Connectivity Sabotage timeout"
              );

          }

          mConnectivityTimeoutMs = timeoutMs;

          final long token = Binder.clearCallingIdentity();

          try {

              Settings.Global.putLong(
                      getContext().getContentResolver(),
                      "secureline_connectivity_timeout",
                      timeoutMs
              );

          } finally {

              Binder.restoreCallingIdentity(token);
          }

              Slog.i(TAG, "Connectivity Sabotage timeout changed: " + timeoutMs);
      }

      @Override

      public int getBruteForceLimit() {
         return mBruteForceLimit;
      }

      @Override

      public void setBruteForceLimit(int limit) {
         enforceSystemCaller();

         if (limit < 8 || limit > 16) {
            throw new IllegalArgumentException("Invalid brute force limit");
         }

         mBruteForceLimit = limit;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putInt(
                     getContext().getContentResolver(),
                     "secureline_bruteforce_limit",
                     limit
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         Slog.i(TAG, "Brute force limit set: " + limit);
      }

      @Override

      public boolean isBfuProtectionEnabled() {
         return mBfuProtectionEnabled;
      }

      @Override

      public void setBfuProtectionEnabled(boolean enabled) {
         enforceSystemCaller();

         mBfuProtectionEnabled = enabled;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putInt(
                     getContext().getContentResolver(),
                     "secureline_bfu_protection",
                     enabled ? 1 : 0
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         if (!enabled) {
            cancelBfuTimer();
         }

         Slog.i(TAG, "BFU protection changed: " + enabled);
      }

      @Override

      public long getBfuTimerTimeoutMs() {
         return mBfuTimerTimeoutMs;
      }

      @Override

      public void setBfuTimerTimeoutMs(long timeoutMs) {
         enforceSystemCaller();

         if (timeoutMs != SecureLinePolicy.BFU_TIMER_1H &&

             timeoutMs != SecureLinePolicy.BFU_TIMER_2H &&

             timeoutMs != SecureLinePolicy.BFU_TIMER_4H &&

             timeoutMs != SecureLinePolicy.BFU_TIMER_6H) {
            throw new IllegalArgumentException("Invalid BFU timer");
         }

         mBfuTimerTimeoutMs = timeoutMs;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putLong(
                     getContext().getContentResolver(),
                     "secureline_bfu_timer",
                     timeoutMs
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         Slog.i(TAG, "BFU timer changed: " + timeoutMs);
      }

      @Override

      public long getInactivityTimeoutMs() {
         return mInactivityTimeoutMs;
      }

      @Override

      public void setInactivityTimeoutMs(long timeoutMs) {
         enforceSystemCaller();

         if (timeoutMs != SecureLinePolicy.INACTIVITY_24H &&

             timeoutMs != SecureLinePolicy.INACTIVITY_48H &&

             timeoutMs != SecureLinePolicy.INACTIVITY_96H &&

             timeoutMs != SecureLinePolicy.INACTIVITY_168H &&

             timeoutMs != SecureLinePolicy.INACTIVITY_336H) {
            throw new IllegalArgumentException("Invalid inactivity timeout");
         }

         mInactivityTimeoutMs = timeoutMs;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putLong(
                     getContext().getContentResolver(),
                     "secureline_inactivity_timeout",
                     timeoutMs
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         Slog.i(TAG, "Inactivity timeout changed: " + timeoutMs);
      }

      @Override

      public boolean isTamperLockEnabled() {
         return mTamperLockEnabled;
      }

      @Override

      public void setTamperLockEnabled(boolean enabled) {
         enforceSystemCaller();

         mTamperLockEnabled = enabled;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putInt(
                     getContext().getContentResolver(),
                     "secureline_tamper_protection",
                     enabled ? 1 : 0
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         Slog.i(TAG, "Tamper lock changed: " + enabled);
      }

      @Override

      public int getTamperSensitivity() {
         return mTamperSensitivity;
      }

      @Override

      public void setTamperSensitivity(int level) {
         enforceSystemCaller();

         if (level < 1 || level > 2)

            throw new IllegalArgumentException("Invalid tamper sensitivity");

         mTamperSensitivity = level;

         final long token = Binder.clearCallingIdentity();

         try {

             Settings.Global.putInt(
                     getContext().getContentResolver(),
                     "secureline_tamper_sensitivity",
                     level
             );

         } finally {

             Binder.restoreCallingIdentity(token);
         }

         Slog.i(TAG, "Tamper sensitivity set: " + level);
      }

      @Override

      public void triggerTamperLock() {
         enforceSystemCaller();

         SecureLineManagerService.this.triggerTamperLock();
      }

      @Override

      public void triggerBfuReboot(String reason) {
         enforceSystemCaller();

         SecureLineManagerService
             .this

             .triggerBfuReboot(reason);
      }

      @Override

      public void notifyDeviceLocked() {
          enforceSystemCaller();
         SecureLineManagerService.this.notifyDeviceLocked();
      }

      @Override

      public void notifyDeviceUnlocked() {
          enforceSystemCaller();
         SecureLineManagerService.this.notifyDeviceUnlocked();
      }

      /*
      * VERIFIED unlock fra LockSettingsService
      */
      @Override

      public void notifyVerifiedUnlock() {
          enforceSystemCaller();
          SecureLineManagerService.this.notifyVerifiedUnlock();
      }

     @Override

     public boolean isDeviceLocked() {
         UserManager um = getContext().getSystemService(UserManager.class);
         KeyguardManager km = getContext().getSystemService(KeyguardManager.class);

         if (um == null || !um.isUserUnlocked()) {
             return true;
         }

         return km == null || km.isDeviceLocked();
     }

     @Override
      public boolean activate(String activationCode) {
         enforceActivationCaller();

         final long callingIdentityToken = Binder.clearCallingIdentity();

         try {
            Slog.i(TAG, "SECURELINE: Activation started");

            ConnectivityManager cm =

                (ConnectivityManager) getContext().getSystemService(
                    Context.CONNECTIVITY_SERVICE);

            Network network = cm.getActiveNetwork();

            if (network == null) {
               Slog.e(TAG, "No active network");

               return false;
            }

            if (activationCode == null || activationCode.length() < 8) {
               Slog.w(TAG, "Invalid activation code");

               return false;
            }

            try {
               Slog.i(TAG, "SECURELINE: Requesting activation nonce");

               String nonce = mActivationClient.requestNonce();

               Slog.i(TAG, "SECURELINE: Nonce received: " + nonce);

               byte[] challenge =

                   Base64.decode(

                       nonce,

                       Base64.URL_SAFE | Base64.NO_WRAP

                   );

               Slog.i(TAG, "SECURELINE: Generating device key and attestation");

               SecureLineDeviceKeyManager.AttestationResult att =

                   mDeviceKeyManager.generateKey(challenge);

               Slog.i(TAG, "SECURELINE: Device key generated");

               byte[] attestationBlob =

                   buildPemAttestation(att.certificateChain);

               Slog.i(TAG, "SECURELINE: Sending activation request to API");

               SecureLineActivationClient.ActivationResponse resp =

                   mActivationClient.activate(

                       activationCode,

                       nonce,

                       attestationBlob

                   );

               Slog.i(TAG, "SECURELINE: Activation API response received");

               Slog.i(

                   TAG,

                   "SECURELINE: Activation response summary device_id="
                       + resp.deviceId

                       + " support_id=" + resp.supportId

                       + " plan=" + resp.plan

                       + " wipe_token_len="
                       + (resp.wipeToken != null ? resp.wipeToken.length() : 0)

                       + " has_license_payload="
                       + (resp.licensePayload != null)

                       + " has_license_signature="
                       + (resp.licenseSignature != null)

               );

               JSONObject identity = new JSONObject();

               identity.put("device_id", resp.deviceId);

               identity.put("support_id", resp.supportId);

               identity.put("plan", resp.plan);

               mIdentityStore.storeIdentity(

                   identity.toString().getBytes(StandardCharsets.UTF_8)

               );

                JSONObject cmd = new JSONObject();

                cmd.put("device_id", resp.deviceId);

                cmd.put("support_id", resp.supportId);

                cmd.put("wipe_token", resp.wipeToken);

                cmd.put("last_command_counter", 0);

                mCommandStore.store(
                    cmd.toString().getBytes(StandardCharsets.UTF_8)
                );

               // ==========================

               // LICENSE FROM ACTIVATION

               // ==========================

               if (resp.licensePayload != null
                   && resp.licenseSignature != null) {
                  if (mLicenseVerifier == null) {
                     Slog.w(TAG,
                         "Activation succeeded without license verification; "
                         + "verifier unavailable");

                  } else {
                     JSONObject license =

                         mLicenseVerifier.verify(

                             resp.licensePayload,

                             resp.licenseSignature,

                             resp.deviceId

                         );

                     Slog.i(

                         TAG,

                         "SECURELINE: Verified activation license payload "
                         + "expires_day="

                             + license.optLong("expires_day", -1)

                             + " license_counter="

                             + license.optLong("license_counter", -1)

                             + " plan="

                             + license.optString("plan", "")

                     );

                     mLicenseStore.updateLicense(license);

                     Slog.i(TAG,

                         "Activation license stored counter=" +

                             license.getLong("license_counter"));

                     Slog.i(

                         TAG,

                         "SECURELINE: Local license after activation active="

                             + mLicenseStore.isLicenseActive()

                             + " days_left="

                             + mLicenseStore.getLicenseDaysLeft()

                             + " expires_day="

                             + mLicenseStore.getExpiresDay()

                             + " now_day="

                             + (System.currentTimeMillis() / 86400000L)

                             + " stored_counter="

                             + mLicenseStore.getLicenseCounter()

                     );
                  }
               }

               // ==========================

               Slog.i(TAG, "Activation stored successfully - awaiting first unlock");

               setStateInternal(
                   SecureLineState.WAITING_FIRST_UNLOCK
              );

                /*
                Device kan allerede være unlocked (AFU)
                ACTION_USER_UNLOCKED sendes kun 1 gang pr boot
                så vi skal manuelt trigge post unlock flow hvis device allerede er unlocked
                */

                mVerifiedUnlockPending = true;

                UserManager um =
                    (UserManager) getContext().getSystemService(Context.USER_SERVICE);

                if (um != null && um.isUserUnlocked()) {

                    Slog.i(
                        TAG,
                        "Activation completed while already AFU → triggering first unlock immediately"
                    );

                    postToWorker(
                        "post_unlock_after_activation",
                        SecureLineManagerService.this::handlePostUserUnlock
                    );
                }

                return true;

            } catch (Exception e) {
               Slog.e(TAG, "SECURELINE: Activation failed", e);

               if (e.getMessage() != null) {
                  Slog.e(TAG,
                      "SECURELINE: Activation error message: "
                          + e.getMessage());
               }

               try {
                  mDeviceKeyManager.deleteDeviceKey();

               } catch (Exception ignored) {
               }

               try {
                  mLicenseStore.deleteLicense();

               } catch (Exception ignored) {
               }

               return false;
            }

         } finally {
            Binder.restoreCallingIdentity(callingIdentityToken);
         }
      }

      private static byte[] buildPemAttestation(java.util.List<byte[]> derChain)

          throws Exception {
         StringBuilder sb = new StringBuilder();

         for (byte[] certDer : derChain) {
            String b64 = Base64.encodeToString(

                certDer,

                Base64.NO_WRAP

            );

            sb.append("-----BEGIN CERTIFICATE-----\n");

            for (int i = 0; i < b64.length(); i += 64) {
               int end = Math.min(i + 64, b64.length());

               sb.append(b64, i, end).append("\n");
            }

            sb.append("-----END CERTIFICATE-----\n");
         }

         return sb.toString().getBytes(StandardCharsets.UTF_8);
      }

      @Override

      public void triggerWipe(int reason) {
         enforceSystemCaller();

         String r;

         switch (reason) {
            case 1:

               r = "usb_data";

               break;

            case 2:

               r = "usb_host";

               break;

            case 3:

               r = "remote_command";

               break;

            case 4:

               r = "brute_force";

               break;

            default:

               r = "unknown";
         }

         triggerImmediateWipe(r);
      }

      private void enforceActivationCaller() {
         int uid = Binder.getCallingUid();

         if (uid == Process.SYSTEM_UID ||

             uid == Process.SHELL_UID) {
            return;
         }

         PackageManager pm = getContext().getPackageManager();

         String[] packages = pm.getPackagesForUid(uid);

         if (packages == null) {
            throw new SecurityException(
                "Only SetupWizard/system may activate SecureLine");
         }

         boolean isSetupWizardUid = false;

         for (String pkg : packages) {
            if (SETUP_WIZARD_PACKAGE.equals(pkg)) {
               isSetupWizardUid = true;

               break;
            }
         }

         if (!isSetupWizardUid ||

             pm.checkSignatures(Process.SYSTEM_UID, uid)
                 != PackageManager.SIGNATURE_MATCH) {
            throw new SecurityException(
                "Only SetupWizard/system may activate SecureLine");
         }
      }
   }

   private static byte[] loadDevicePublicKeyBytes() throws Exception {
      KeyStore ks = KeyStore.getInstance("AndroidKeyStore");

      ks.load(null);

      Certificate cert = ks.getCertificate(DEVICE_KEY_ALIAS);

      if (cert == null)
         return null;

      PublicKey pk = cert.getPublicKey();

      return pk != null ? pk.getEncoded() : null;
   }
}
