/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.car.testapi;

import static com.android.car.power.CarPowerManagementService.powerStateToString;

import android.annotation.Nullable;
import android.automotive.power.internal.ICarPowerManagementDelegate;
import android.automotive.power.internal.ICarPowerManagementDelegateCallback;
import android.automotive.power.internal.PowerPolicyInitData;
import android.car.builtin.util.Slogf;
import android.car.hardware.power.PowerComponent;
import android.car.hardware.power.PowerComponentUtil;
import android.frameworks.automotive.powerpolicy.CarPowerPolicy;
import android.os.FileObserver;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.car.CarServiceUtils;
import com.android.internal.annotations.GuardedBy;

import libcore.io.IoUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Fake power management daemon to be used in car service test and car service unit test when
 * refactored power policy logic is used.
 */
public final class FakeRefactoredCarPowerManagementDaemon extends
        ICarPowerManagementDelegate.Default {
    private static final String TAG = FakeRefactoredCarPowerManagementDaemon.class.getSimpleName();

    /**
     * The power policy ID for the system power policy for all power components on; the default
     * power policy for the "ON" state.
     */
    public static final String SYSTEM_POWER_POLICY_ALL_ON = "system_power_policy_all_on";
    /**
     * The power policy ID for the system power policy for no user interaction; the default
     * power policy for silent mode and shutdown preparation states.
     */
    public static final String SYSTEM_POWER_POLICY_NO_USER_INTERACTION =
            "system_power_policy_no_user_interaction";
    /**
     * The power policy ID for the system power policy for initial on power components; the default
     * power policy for the "WAIT_FOR_VHAL" state.
     */
    public static final String SYSTEM_POWER_POLICY_INITIAL_ON = "system_power_policy_initial_on";
    /**
     * The power policy ID for the system power policy for suspend preparation.
     */
    public static final String SYSTEM_POWER_POLICY_SUSPEND_PREP =
            "system_power_policy_suspend_prep";

    private static final String POLICY_PER_STATE_GROUP_ID = "default_policy_per_state";

    private final Object mLock = new Object();
    private final int[] mCustomComponents;
    private final FileObserver mFileObserver;
    private final File mFileKernelSilentMode;
    private final ComponentHandler mComponentHandler = new ComponentHandler();
    private final Handler mHandler;
    private final Map<String, CarPowerPolicy> mPolicies = new ArrayMap<>();
    private final Map<String, SparseArray<CarPowerPolicy>> mPowerPolicyGroups = new ArrayMap<>();
    private final ReentrantLock mNotifiedStateLock = new ReentrantLock();
    private final Condition mNotifiedStateCondition = mNotifiedStateLock.newCondition();
    @GuardedBy("mLock")
    private final ArrayList<Integer> mNotifiedPowerStates = new ArrayList<>();
    @GuardedBy("mLock")
    private final SparseIntArray mPowerStateChangeIds = new SparseIntArray();

    private String mLastSetPowerPolicyGroupId = POLICY_PER_STATE_GROUP_ID;
    private String mPendingPowerPolicyId;
    private String mLastDefinedPolicyId;
    private String mCurrentPowerPolicyId = SYSTEM_POWER_POLICY_INITIAL_ON;
    private boolean mSilentModeOn;
    private boolean mHasPowerStateListenersWithCompletion = false;
    private boolean mNotifyPowerStateChangeThrowsIllegalArgumentException;
    private boolean mNotifyPowerStateChangeThrowsSecurityException;
    private boolean mNotifyPowerStateChangeThrowsRemoteException;
    @GuardedBy("mLock")
    private ICarPowerManagementDelegateCallback mCallback;

    public FakeRefactoredCarPowerManagementDaemon(@Nullable File fileKernelSilentMode,
            @Nullable int[] customComponents) throws Exception {
        HandlerThread handlerThread = CarServiceUtils.getHandlerThread(TAG);
        mHandler = new Handler(handlerThread.getLooper());
        mFileKernelSilentMode = (fileKernelSilentMode == null)
                ? new File("KERNEL_SILENT_MODE") : fileKernelSilentMode;
        mFileObserver = new SilentModeFileObserver(mFileKernelSilentMode, FileObserver.CLOSE_WRITE);
        CarPowerPolicy policyInitialOn = createInitialOnPowerPolicy();
        CarPowerPolicy policyAllOn = createAllOnPowerPolicy();
        CarPowerPolicy policyNoUser = createNoUserPowerPolicy();
        CarPowerPolicy policySuspendPrep = createSuspendPrepPowerPolicy();
        mPolicies.put(SYSTEM_POWER_POLICY_INITIAL_ON, policyInitialOn);
        mPolicies.put(SYSTEM_POWER_POLICY_ALL_ON, policyAllOn);
        mPolicies.put(SYSTEM_POWER_POLICY_NO_USER_INTERACTION, policyNoUser);
        mPolicies.put(SYSTEM_POWER_POLICY_SUSPEND_PREP, policySuspendPrep);
        mPowerPolicyGroups.put(POLICY_PER_STATE_GROUP_ID, createPolicyGroup(
                SYSTEM_POWER_POLICY_INITIAL_ON, SYSTEM_POWER_POLICY_ALL_ON));
        mCustomComponents = Objects.requireNonNullElse(customComponents, new int[]{});
    }

    private static CarPowerPolicy createPolicy(
            String policyId, int[] enabledComponents, int[] disabledComponents) {
        CarPowerPolicy policy = new CarPowerPolicy();
        policy.policyId = policyId;
        policy.enabledComponents = enabledComponents;
        policy.disabledComponents = disabledComponents;
        return policy;
    }

    // Create a fake system_power_policy_initial_on
    private static CarPowerPolicy createInitialOnPowerPolicy() {
        return createPolicy(SYSTEM_POWER_POLICY_INITIAL_ON,
                new int[]{PowerComponent.AUDIO, PowerComponent.DISPLAY, PowerComponent.CPU},
                new int[]{PowerComponent.MEDIA, PowerComponent.BLUETOOTH,
                        PowerComponent.WIFI, PowerComponent.CELLULAR,
                        PowerComponent.ETHERNET, PowerComponent.PROJECTION,
                        PowerComponent.NFC, PowerComponent.INPUT,
                        PowerComponent.VOICE_INTERACTION,
                        PowerComponent.VISUAL_INTERACTION,
                        PowerComponent.TRUSTED_DEVICE_DETECTION,
                        PowerComponent.LOCATION, PowerComponent.MICROPHONE});
    }

    // Create a fake system_power_policy_all_on
    private static CarPowerPolicy createAllOnPowerPolicy() {
        return createPolicy(SYSTEM_POWER_POLICY_ALL_ON, new int[]{PowerComponent.AUDIO,
                PowerComponent.MEDIA, PowerComponent.DISPLAY, PowerComponent.BLUETOOTH,
                PowerComponent.WIFI, PowerComponent.CELLULAR, PowerComponent.ETHERNET,
                PowerComponent.PROJECTION, PowerComponent.NFC, PowerComponent.INPUT,
                PowerComponent.VOICE_INTERACTION, PowerComponent.VISUAL_INTERACTION,
                PowerComponent.TRUSTED_DEVICE_DETECTION, PowerComponent.LOCATION,
                PowerComponent.MICROPHONE, PowerComponent.CPU}, new int[]{});
    }

    // Create a fake system_power_policy_no_user_interaction
    private static CarPowerPolicy createNoUserPowerPolicy() {
        return createPolicy(SYSTEM_POWER_POLICY_NO_USER_INTERACTION,
                new int[]{
                        PowerComponent.WIFI, PowerComponent.CELLULAR, PowerComponent.ETHERNET,
                        PowerComponent.CPU, PowerComponent.TRUSTED_DEVICE_DETECTION},
                new int[]{
                        PowerComponent.AUDIO, PowerComponent.MEDIA, PowerComponent.DISPLAY,
                        PowerComponent.BLUETOOTH, PowerComponent.PROJECTION, PowerComponent.NFC,
                        PowerComponent.INPUT, PowerComponent.VOICE_INTERACTION,
                        PowerComponent.VISUAL_INTERACTION, PowerComponent.LOCATION,
                        PowerComponent.MICROPHONE});
    }

    // Create a fake system_power_policy_suspend_prep
    private static CarPowerPolicy createSuspendPrepPowerPolicy() {
        return createPolicy(SYSTEM_POWER_POLICY_SUSPEND_PREP, new int[]{}, new int[]{
                PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI,
                PowerComponent.LOCATION, PowerComponent.MICROPHONE, PowerComponent.CPU});
    }

    private SparseArray<CarPowerPolicy> createPolicyGroup(String waitForVhalPolicyId,
            String onPolicyId) {
        SparseArray<CarPowerPolicy> policyGroup = new SparseArray<>();
        policyGroup.put(PowerState.WAIT_FOR_VHAL, mPolicies.get(waitForVhalPolicyId));
        policyGroup.put(PowerState.ON, mPolicies.get(onPolicyId));
        return policyGroup;
    }

    @Override
    public PowerPolicyInitData notifyCarServiceReady(ICarPowerManagementDelegateCallback callback) {
        Log.i(TAG, "Fake refactored CPPD was notified that car service is ready");
        synchronized (mLock) {
            mCallback = callback;
        }
        PowerPolicyInitData initData = new PowerPolicyInitData();
        initData.currentPowerPolicy = mPolicies.get(mCurrentPowerPolicyId);
        initData.registeredPolicies = new CarPowerPolicy[]{
                mPolicies.get(SYSTEM_POWER_POLICY_INITIAL_ON),
                mPolicies.get(SYSTEM_POWER_POLICY_ALL_ON),
                mPolicies.get(SYSTEM_POWER_POLICY_NO_USER_INTERACTION),
                mPolicies.get(SYSTEM_POWER_POLICY_SUSPEND_PREP)};
        initData.registeredCustomComponents = mCustomComponents;
        mComponentHandler.applyPolicy(mPolicies.get(mCurrentPowerPolicyId));
        return initData;
    }

    @Override
    public void applyPowerPolicyAsync(int requestId, String policyId, boolean force)
            throws RemoteException {
        Log.i(TAG, "Fake refactored CPPD is attempting to apply power policy " + policyId);

        boolean deferred = isPreemptivePolicy(mCurrentPowerPolicyId)
                && !isPreemptivePolicy(policyId);
        CarPowerPolicy currentPolicy = mPolicies.get(policyId);
        if (currentPolicy == null) {
            throw new IllegalArgumentException("Power policy " + policyId + " is invalid");
        }
        mComponentHandler.applyPolicy(currentPolicy);
        CarPowerPolicy accumulatedPolicy = mComponentHandler.getAccumulatedPolicy(policyId);
        ICarPowerManagementDelegateCallback callback = getPowerManagementDelegateCallback();
        mHandler.post(() -> {
            try {
                callback.updatePowerComponents(accumulatedPolicy);
                callback.onApplyPowerPolicySucceeded(requestId, accumulatedPolicy, deferred);
                if (!deferred) {
                    mCurrentPowerPolicyId = policyId;
                }
            } catch (Exception e) {
                Log.w(TAG, "Cannot call onApplyPowerPolicySucceeded", e);
            }
        });
    }

    @Override
    public void applyPowerPolicyPerPowerStateChangeAsync(int requestId, int state)
            throws RemoteException {
        CarPowerPolicy policy = mPowerPolicyGroups.get(mLastSetPowerPolicyGroupId).get(state);
        if (policy == null) {
            throw new IllegalArgumentException("No default policy defined for state " + state);
        }
        ICarPowerManagementDelegateCallback callback = getPowerManagementDelegateCallback();
        if (mSilentModeOn) {
            mHandler.post(() -> {
                mPendingPowerPolicyId = policy.policyId;

                Log.d(TAG, "Silent mode is on, so applying power policy for state " + state
                        + " is deferred, setting pending power policy to " + mPendingPowerPolicyId);
                try {
                    callback.onApplyPowerPolicySucceeded(requestId, policy, /* deferred= */ true);
                } catch (Exception e) {
                    Log.w(TAG, "Cannot call onApplyPowerPolicySucceeded", e);
                }
            });
            return;
        }
        mHandler.post(() -> {
            try {
                callback.updatePowerComponents(policy);
                callback.onApplyPowerPolicySucceeded(requestId, policy, /* deferred= */ false);
                synchronized (mLock) {
                    mNotifiedPowerStates.add(state);
                }
                mComponentHandler.applyPolicy(policy);
                mCurrentPowerPolicyId = policy.policyId;
            } catch (Exception e) {
                Log.w(TAG, "Cannot call onApplyPowerPolicySucceeded", e);
            }
        });
    }

    /**
     * Set if notifyPowerStateChange throws an IllegalArgumentException
     * @param throwsException True if should throw IllegalArgumentException
     */
    public void notifyPowerStateChangeThrowsIllegalArgumentException(boolean throwsException) {
        mNotifyPowerStateChangeThrowsIllegalArgumentException = throwsException;
    }

    /**
     * Set if notifyPowerStateChange throws an SecurityException
     * @param throwsException True if should throw SecurityException
     */
    public void notifyPowerStateChangeThrowsSecurityException(boolean throwsException) {
        mNotifyPowerStateChangeThrowsSecurityException = throwsException;
    }

    /**
     * Set if notifyPowerStateChange throws an RemoteException
     * @param throwsException True if should throw RemoteException
     */
    public void notifyPowerStateChangeThrowsRemoteException(boolean throwsException) {
        mNotifyPowerStateChangeThrowsRemoteException = throwsException;
    }

    @Override
    public void notifyPowerStateChange(int changeId, int newState, long expirationTimeMs)
            throws RemoteException {
        String stateName = powerStateToString(newState);
        Slogf.d(TAG, "Daemon has been notified of state change to %s with expiration of %d ms",
                stateName, expirationTimeMs);
        if (mNotifyPowerStateChangeThrowsIllegalArgumentException) {
            throw new IllegalArgumentException();
        } else if (mNotifyPowerStateChangeThrowsSecurityException) {
            throw new SecurityException();
        } else if (mNotifyPowerStateChangeThrowsRemoteException) {
            throw new RemoteException();
        }
        synchronized (mLock) {
            mNotifiedPowerStates.add(newState);
            mPowerStateChangeIds.put(newState, changeId);
        }
        if (!mHasPowerStateListenersWithCompletion) {
            setAllPowerStateChangeListenersComplete(newState);
        }
        mNotifiedStateLock.lock();
        try {
            mNotifiedStateCondition.signalAll();
        } finally {
            mNotifiedStateLock.unlock();
        }
    }

    /**
     * Set if notifying power state change waits for completion before notifying to CPMS that all
     * listeners completed or timed out.
     * @param hasListeners Whether the daemon should behave as if it has power state change
     *                     listeners with completion registered. If true, caller will have to
     *                     designate listeners as complete by using
     *                     {@link setAllPowerStateChangeListenersComplete}
     */
    public void setHasListenersWithCompletion(boolean hasListeners) {
        mHasPowerStateListenersWithCompletion = hasListeners;
    }

    /**
     * Initiate a call to onAllPowerStateChangeListenersComplete for the given state
     * @param state State for which all listeners should be considered complete
     */
    public void setAllPowerStateChangeListenersComplete(int state) {
        ICarPowerManagementDelegateCallback callback;
        int changeId = -1;
        synchronized (mLock) {
            if (mCallback == null) {
                Slogf.i(TAG, "notifyCarServiceReady is not called yet. Ignoring power state "
                        + "change");
                return;
            }
            callback = mCallback;
            changeId = mPowerStateChangeIds.get(state, -1);
        }
        try {
            callback.onAllPowerStateChangeListenersComplete(changeId);
        } catch (RemoteException e) {
            Slogf.w(TAG, "Cannot call onAllPowerStateChangeListenersComplete", e);
        }
    }

    /**
     * Wait for a power state change to be notified to the daemon
     * @param state The state to wait for
     * @param timeoutMs The max amount of time to wait for in milliseconds
     * @return True if the state was notified, false otherwise
     * @throws InterruptedException if awaiting state notification is interrupted
     */
    public boolean waitForStateNotified(int state, long timeoutMs) throws InterruptedException {
        boolean stateNotified = false;
        synchronized (mLock) {
            stateNotified = mNotifiedPowerStates.getLast().equals(state);
        }
        while (!stateNotified && timeoutMs > 0) {
            long startTimeMs = SystemClock.elapsedRealtime();
            mNotifiedStateLock.lock();
            try {
                mNotifiedStateCondition.await(timeoutMs, TimeUnit.MILLISECONDS);
                synchronized (mLock) {
                    stateNotified = mNotifiedPowerStates.getLast().equals(state);
                }
            } finally {
                mNotifiedStateLock.unlock();
            }
            timeoutMs -= SystemClock.elapsedRealtime() - startTimeMs;
        }
        return stateNotified;
    }

    private void applyPowerPolicyInternal(String policyId, String errMsg) {
        mComponentHandler.applyPolicy(mPolicies.get(policyId));
        CarPowerPolicy accumulatedPolicy = mComponentHandler.getAccumulatedPolicy(policyId);
        mHandler.post(() -> {
            try {
                ICarPowerManagementDelegateCallback callback = getPowerManagementDelegateCallback();
                callback.onPowerPolicyChanged(accumulatedPolicy);
                callback.updatePowerComponents(accumulatedPolicy);
                mCurrentPowerPolicyId = policyId;
            } catch (RemoteException e) {
                Log.d(TAG, errMsg, e);
            }
        });
    }

    private int[] convertIntIterableToArray(Iterable<Integer> iterable) {
        List<Integer> list = new ArrayList<>();
        iterable.forEach(list::add);
        return list.stream().mapToInt(i->i).toArray();
    }

    @Override
    public void notifyPowerPolicyDefinition(String policyId, String[] enabledComponents,
                                            String[] disabledComponents) {
        mLastDefinedPolicyId = policyId;
        CarPowerPolicy policy = new CarPowerPolicy();
        policy.policyId = policyId;
        policy.enabledComponents = convertIntIterableToArray(
                PowerComponentUtil.toPowerComponents(List.of(enabledComponents),
                        /* prefix= */ false));
        policy.disabledComponents = convertIntIterableToArray(
                PowerComponentUtil.toPowerComponents(List.of(disabledComponents),
                        /* prefix= */ false));
        mPolicies.put(policyId, policy);
    }

    @Override
    public void notifyPowerPolicyGroupDefinition(
            String policyGroupId, String[] powerPolicyPerState) {
        String waitForVhalPolicyId = powerPolicyPerState[0];
        if (mPolicies.get(waitForVhalPolicyId) == null) {
            throw new IllegalArgumentException(
                    "No registered policy with ID " + waitForVhalPolicyId);
        }
        String onPolicyId = powerPolicyPerState[1];
        if (mPolicies.get(onPolicyId) == null) {
            throw new IllegalArgumentException(
                    "No registered policy with ID " + onPolicyId);
        }
        mPowerPolicyGroups.put(
                policyGroupId, createPolicyGroup(waitForVhalPolicyId, onPolicyId));
        SparseArray<CarPowerPolicy> policyGroup = new SparseArray<>();
        policyGroup.put(PowerState.WAIT_FOR_VHAL, mPolicies.get(waitForVhalPolicyId));
        policyGroup.put(PowerState.ON, mPolicies.get(onPolicyId));
        mPowerPolicyGroups.put(policyGroupId, policyGroup);
    }

    /**
     * Get the current power policy
     * @return The current power policy
     */
    public CarPowerPolicy getCurrentPowerPolicy() {
        return mPolicies.get(mCurrentPowerPolicyId);
    }

    @Override
    public void setPowerPolicyGroup(String policyGroupId) {
        if (mPowerPolicyGroups.containsKey(policyGroupId)) {
            mLastSetPowerPolicyGroupId = policyGroupId;
        } else {
            throw new IllegalArgumentException("Policy group " + policyGroupId + " doesn't exist");
        }
    }

    /**
     * Get the last power state notified to the daemon
     * @return Last notified power state
     */
    public int getLastNotifiedPowerState() {
        synchronized (mLock) {
            try {
                return mNotifiedPowerStates.getLast();
            } catch (NoSuchElementException e) {
                return -1;
            }
        }
    }

    /**
     * Get the power states that have been notified to the daemon in the order they were received.
     * @return Power states notified to daemon.
     */
    public List<Integer> getNotifiedPowerStates() {
        synchronized (mLock) {
            return new ArrayList<>(mNotifiedPowerStates);
        }
    }

    /**
     * Clear the list of power states that have been notified to the daemon.
     */
    public void clearNotifiedPowerStates() {
        synchronized (mLock) {
            mNotifiedPowerStates.clear();
        }
    }

    public String getLastDefinedPolicyId() {
        return mLastDefinedPolicyId;
    }

    /**
     * Begin observing the silent mode file for changes in silent mode state. Should be called
     * at the beginning of a test case that involves silent mode and power policy.
     */
    public void silentModeFileObserverStartWatching() {
        mFileObserver.startWatching();
    }

    /**
     * Stop observing the silent mode file. Should be called at the end of a test case where
     * {@link #silentModeFileObserverStopWatching()} was called.
     */
    public void silentModeFileObserverStopWatching() {
        mFileObserver.stopWatching();
    }

    private ICarPowerManagementDelegateCallback getPowerManagementDelegateCallback() {
        synchronized (mLock) {
            if (mCallback == null) {
                throw new IllegalStateException("Fake refactored CPPD callback is null, was "
                        + "notifyCarServiceReady() called?");
            }
            return mCallback;
        }
    }

    private boolean isPreemptivePolicy(String policyId) {
        return Objects.equals(policyId, SYSTEM_POWER_POLICY_NO_USER_INTERACTION)
                || Objects.equals(policyId, SYSTEM_POWER_POLICY_SUSPEND_PREP);
    }

    private final class SilentModeFileObserver extends FileObserver {
        SilentModeFileObserver(File file, int mask) {
            super(file, mask);
        }

        @Override
        public void onEvent(int event, @Nullable String path) {
            boolean silent;
            try {
                String contents = IoUtils.readFileAsString(
                        mFileKernelSilentMode.getPath().toString()).trim();
                silent = contents.equals("1");
            } catch (Exception e) {
                Log.w(TAG, "Fake CPPD couldn't read kernel silent mode file");
                return;
            }
            Log.d(TAG, "Fake CPPD observed change in silent mode kernel file, silent = "
                    + silent);
            if (silent) {
                mSilentModeOn = true;
                Log.d(TAG, "Fake CPPD attempting to apply silent power policy");
                applyPowerPolicyInternal(SYSTEM_POWER_POLICY_NO_USER_INTERACTION,
                        "Fake CPPD failed to apply silent power policy");
            } else {
                mSilentModeOn = false;
                Log.d(TAG, "Fake CPPD attempting to apply pending power policy");
                if (mPendingPowerPolicyId != null) {
                    applyPowerPolicyInternal(mPendingPowerPolicyId,
                            "Fake CPPD failed to apply pending power policy");
                }
            }
        }
    }

    private static final class ComponentHandler {
        private final IntArray mEnabledComponents = new IntArray();
        private final IntArray mDisabledComponents = new IntArray();

        CarPowerPolicy getAccumulatedPolicy(String policyId) {
            CarPowerPolicy policy = new CarPowerPolicy();
            policy.policyId = policyId;
            policy.enabledComponents = mEnabledComponents.toArray();
            policy.disabledComponents = mDisabledComponents.toArray();
            return policy;
        }

        void applyPolicy(CarPowerPolicy policy) {
            int[] enabledComponents = policy.enabledComponents;
            for (int i = 0; i < enabledComponents.length; i++) {
                int component = enabledComponents[i];
                if (!mEnabledComponents.contains(component)) {
                    if (mDisabledComponents.contains(component)) {
                        mDisabledComponents.remove(mDisabledComponents.indexOf(component));
                    }
                    mEnabledComponents.add(component);
                }
            }
            int[] disabledComponents = policy.disabledComponents;
            for (int i = 0; i < disabledComponents.length; i++) {
                int component = disabledComponents[i];
                if (!mDisabledComponents.contains(component)) {
                    if (mEnabledComponents.contains(component)) {
                        mEnabledComponents.remove(mEnabledComponents.indexOf(component));
                    }
                    mDisabledComponents.add(component);
                }
            }
        }
    }
}
