class WorkerWrapper implements Runnable {

    @WorkerThread
    @Override
    public void run() {
        if (tryCheckForInterruptionAndNotify()) {
            return;
        }

        mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
        if (mWorkSpec == null) {
            Log.e(TAG,  String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
            notifyListener(false, false);
            return;
        }

        // Do a quick check to make sure we don't need to bail out in case this work is already
        // running, finished, or is blocked.
        if (mWorkSpec.state != ENQUEUED) {
            notifyIncorrectStatus();
            return;
        }

        // Merge inputs.  This can be potentially expensive code, so this should not be done inside
        // a database transaction.
        Data input;
        if (mWorkSpec.isPeriodic()) {
            input = mWorkSpec.input;
        } else {
            InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);
            if (inputMerger == null) {
                Log.e(TAG, String.format("Could not create Input Merger %s",
                        mWorkSpec.inputMergerClassName));
                setFailedAndNotify();
                return;
            }
            List<Data> inputs = new ArrayList<>();
            inputs.add(mWorkSpec.input);
            inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
            input = inputMerger.merge(inputs);
        }

        Extras extras = new Extras(
                input,
                mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId),
                mRuntimeExtras,
                mWorkSpec.runAttemptCount);

        // Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
        // in test mode.
        if (mWorker == null) {
            mWorker = workerFromWorkSpec(mAppContext, mWorkSpec, extras);
        }

        if (mWorker == null) {
            Log.e(TAG, String.format("Could for create Worker %s", mWorkSpec.workerClassName));
            setFailedAndNotify();
            return;
        }

        // Try to set the work to the running state.  Note that this may fail because another thread
        // may have modified the DB since we checked last at the top of this function.
        if (trySetRunning()) {
            if (tryCheckForInterruptionAndNotify()) {
                return;
            }

            Worker.Result result;
            try {
                result = mWorker.doWork();
            } catch (Exception | Error e) {
                result = Worker.Result.FAILURE;
            }

            try {
                mWorkDatabase.beginTransaction();
                if (!tryCheckForInterruptionAndNotify()) {
                    State state = mWorkSpecDao.getState(mWorkSpecId);
                    if (state == null) {
                        // state can be null here with a REPLACE on beginUniqueWork().
                        // Treat it as a failure, and rescheduleAndNotify() will
                        // turn into a no-op. We still need to notify potential observers
                        // holding on to wake locks on our behalf.
                        notifyListener(false, false);
                    } else if (state == RUNNING) {
                        handleResult(result);
                    } else if (!state.isFinished()) {
                        rescheduleAndNotify();
                    }
                    mWorkDatabase.setTransactionSuccessful();
                }
            } finally {
                mWorkDatabase.endTransaction();
            }
        } else {
            notifyIncorrectStatus();
        }
    }

    private  WorkerWrapper(Builder builder);

}