Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20f3c697 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Addressing SecureLockDeviceService feedback / cleanup" into main

parents 798e5cd0 6f5e61e7
Loading
Loading
Loading
Loading
+100 −1073

File changed.

Preview size limit exceeded, changes collapsed.

+153 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.security.authenticationpolicy.settings;

import static android.os.UserManager.DISALLOW_DEBUGGING_FEATURES;
import static android.os.UserManager.DISALLOW_USB_FILE_TRANSFER;

import android.app.admin.DevicePolicyManager;
import android.os.Bundle;
import android.util.Log;
import android.util.Slog;

import androidx.annotation.NonNull;

import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/** Setting controller for applying and clearing device policy restrictions. */
public class DevicePolicyRestrictionsController implements SettingController<Set<String>> {
    public static final String TAG = "DevicePolicyController";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    static final String DEVICE_POLICY_RESTRICTIONS_KEY = "device_policy_restrictions";
    static final String DEVICE_POLICY_ELEMENT_KEY = "restriction";
    private final Set<String> mSkipDuringTest = Set.of(DISALLOW_USB_FILE_TRANSFER,
            DISALLOW_DEBUGGING_FEATURES);
    private DevicePolicyManager mDevicePolicyManager;
    private boolean mSkipSecurityFeaturesForTest = false;

    @Override
    public void storeOriginalValue(@NonNull SettingState<Set<String>> state, int userId)
            throws Exception {
        if (mDevicePolicyManager == null) {
            Slog.w(TAG, "DevicePolicyManager is null, cannot retrieve original value for "
                    + "device policy restrictions.");
            return;
        }
        state.setOriginalValue(bundleToSet(mDevicePolicyManager.getUserRestrictionsGlobally()));
    }

    @Override
    public void applySecureLockDeviceValue(@NonNull SettingState<Set<String>> state, int userId)
            throws Exception {
        if (mDevicePolicyManager == null) {
            Slog.w(TAG, "DevicePolicyManager is null, cannot apply device policy restrictions.");
            return;
        }

        for (String restriction : state.getSecureLockDeviceValue()) {
            if (mSkipSecurityFeaturesForTest && mSkipDuringTest.contains(restriction)) {
                Slog.d(TAG, "Skipping applying restriction " + restriction + " for test.");
                continue;
            }

            mDevicePolicyManager.addUserRestrictionGlobally(TAG, restriction);
        }
    }

    @Override
    public void restoreFromOriginalValue(@NonNull SettingState<Set<String>> state, int userId)
            throws Exception {
        if (mDevicePolicyManager == null) {
            Slog.w(TAG, "DevicePolicyManager is null, cannot restore device policy "
                    + "restrictions.");
            return;
        } else if (state.getOriginalValue() == null) {
            Slog.w(TAG, "Original value for device policy restrictions is null, cannot restore "
                    + "device policy restrictions.");
            return;
        }

        Set<String> originalValue = state.getOriginalValue();
        for (String restriction : state.getSecureLockDeviceValue()) {
            if (mSkipSecurityFeaturesForTest && mSkipDuringTest.contains(restriction)) {
                Slog.d(TAG, "Skipping restoring restriction " + restriction + " for test.");
                continue;
            }

            if (originalValue.contains(restriction)) {
                Slog.i(TAG, "Restriction " + restriction + " was already set prior to "
                        + "secure lock device, leave unchanged.");
            } else {
                if (DEBUG) {
                    Slog.i(TAG, "Restriction " + restriction + " was not set prior to "
                            + "secure lock device, clear restriction.");
                }
                mDevicePolicyManager.clearUserRestrictionGlobally(TAG, restriction);
            }
        }
    }

    @Override
    public void serializeOriginalValue(@NonNull String settingKey,
            @NonNull Set<String> originalValue, @NonNull TypedXmlSerializer serializer)
            throws XmlPullParserException, IOException {
        XmlUtils.writeSetXml(originalValue, DEVICE_POLICY_ELEMENT_KEY, serializer);
    }

    @Override
    public Set<String> deserializeOriginalValue(@NonNull TypedXmlPullParser parser,
            @NonNull String settingKey) throws IOException, XmlPullParserException {
        HashSet<String> hashSet = XmlUtils.readThisSetXml(parser, "set", null);
        return hashSet;
    }

    /**
     * Sets whether to skip security features for test.
     *
     * @param skipSecurityFeaturesForTest Whether to skip security features for test.
     */
    void setSkipSecurityFeaturesForTest(boolean skipSecurityFeaturesForTest) {
        mSkipSecurityFeaturesForTest = skipSecurityFeaturesForTest;
    }

    /**
     * Sets the DevicePolicyManager.
     *
     * @param devicePolicyManager The DevicePolicyManager.
     */
    void setDevicePolicyManager(DevicePolicyManager devicePolicyManager) {
        mDevicePolicyManager = devicePolicyManager;
    }

    private Set<String> bundleToSet(Bundle restrictions) {
        Set<String> set = new HashSet<>();
        for (String key : restrictions.keySet()) {
            if (restrictions.getBoolean(key)) {
                set.add(key);
            }
        }
        return set;
    }
}
+125 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.security.authenticationpolicy.settings;

import android.os.Binder;
import android.os.IBinder;
import android.util.Pair;
import android.util.Slog;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

/**
 * Setting controller for disabling status bar features. When secure lock device is enabled,
 * disables status bar expansion, notifications, home button, back gestures, search gestures, and
 * call chips.
 */
class DisableFlagsController implements SettingController<Pair<Integer, Integer>> {
    private static final String TAG = "DisableFlagsController";
    private final IBinder mToken = new LockTaskToken();
    private final String mPackageName;
    private final IStatusBarService mStatusBarService;

    DisableFlagsController(@NonNull String packageName,
            @Nullable IStatusBarService statusBarService) {
        mStatusBarService = statusBarService;
        mPackageName = packageName;
    }

    @Override
    public void storeOriginalValue(@NonNull SettingState<Pair<Integer, Integer>> state, int userId)
            throws Exception {
        if (mStatusBarService == null) {
            throw new Exception("IStatusBarService is null, cannot retrieve status bar state.");
        }

        int[] disableInfo = mStatusBarService.getDisableFlags(mToken, userId);
        state.setOriginalValue(new Pair<>(disableInfo[0], disableInfo[1]));
    }

    @Override
    public void applySecureLockDeviceValue(@NonNull SettingState<Pair<Integer, Integer>> state,
            int userId) throws Exception {
        if (mStatusBarService == null) {
            Slog.w(TAG, "IStatusBarService is null, cannot apply secure lock device value for "
                    + "status bar state.");
            return;
        }
        Pair<Integer, Integer> secureLockDeviceValue = state.getSecureLockDeviceValue();
        mStatusBarService.disable(secureLockDeviceValue.first, mToken, mPackageName);
        mStatusBarService.disable2(secureLockDeviceValue.second, mToken, mPackageName);
    }

    @Override
    public void restoreFromOriginalValue(@NonNull SettingState<Pair<Integer, Integer>> state,
            int userId) throws Exception {
        if (mStatusBarService == null) {
            Slog.w(TAG, "IStatusBarService is null, cannot restore original status bar state.");
            return;
        }

        Pair<Integer, Integer> originalValue = state.getOriginalValue();
        if (originalValue != null) {
            mStatusBarService.disable(originalValue.first, mToken, mPackageName);
            mStatusBarService.disable2(originalValue.second, mToken, mPackageName);
        }
    }

    @Override
    public void serializeOriginalValue(@NonNull String settingKey,
            @NonNull Pair<Integer, Integer> originalValue, @NonNull TypedXmlSerializer serializer)
            throws IOException {
        serializer.startTag(null, "disable1")
                .text(Integer.toString(originalValue.first))
                .endTag(null, "disable1");
        serializer.startTag(null, "disable2")
                .text(Integer.toString(originalValue.second))
                .endTag(null, "disable2");
    }

    @Override
    public Pair<Integer, Integer> deserializeOriginalValue(@NonNull TypedXmlPullParser parser,
            @NonNull String settingKey) throws IOException, XmlPullParserException {
        int disable1Val = 0;
        int disable2Val = 0;
        int disableFlagsDepth = parser.getDepth();
        while (XmlUtils.nextElementWithin(parser, disableFlagsDepth)) {
            if ("disable1".equals(parser.getName())) {
                disable1Val = Integer.parseInt(parser.nextText());
            } else if ("disable2".equals(parser.getName())) {
                disable2Val = Integer.parseInt(parser.nextText());
            } else {
                XmlUtils.skipCurrentTag(parser);
            }
        }
        return new Pair<>(disable1Val, disable2Val);
    }

    /** Marker class for the token used to disable keyguard. */
    private static class LockTaskToken extends Binder {
    }
}
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.security.authenticationpolicy.settings;

import android.content.ContentResolver;
import android.provider.Settings;
import android.util.Slog;

import androidx.annotation.NonNull;

import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

/** Setting controller for {@link Settings.Global} settings. */
class GlobalSettingController implements SettingController<Integer> {
    private static final String TAG = "GlobalSettingController";

    private final ContentResolver mContentResolver;
    private boolean mSkipSecurityFeaturesForTest = false;

    GlobalSettingController(@NonNull ContentResolver contentResolver) {
        mContentResolver = contentResolver;
    }

    @Override
    public void storeOriginalValue(@NonNull SettingState<Integer> state, int userId)
            throws Exception {
        if (mSkipSecurityFeaturesForTest) {
            Slog.d(TAG, "Skipping storing global settings for test.");
            return;
        }
        int originalValue = Settings.Global.getInt(mContentResolver, state.getSettingKey());
        state.setOriginalValue(originalValue);
    }

    @Override
    public void applySecureLockDeviceValue(@NonNull SettingState<Integer> state, int userId)
            throws Exception {
        if (mSkipSecurityFeaturesForTest) {
            Slog.d(TAG, "Skipping applying global settings for test.");
            return;
        }
        int secureLockDeviceValue = state.getSecureLockDeviceValue();
        Settings.Global.putInt(mContentResolver, state.getSettingKey(), secureLockDeviceValue);
    }

    @Override
    public void restoreFromOriginalValue(@NonNull SettingState<Integer> state, int userId)
            throws Exception {
        if (mSkipSecurityFeaturesForTest) {
            Slog.d(TAG, "Skipping restoring global settings for test.");
            return;
        }
        Integer originalValue = state.getOriginalValue();
        if (originalValue != null) {
            Settings.Global.putInt(mContentResolver, state.getSettingKey(), originalValue);
        }
    }

    @Override
    public void serializeOriginalValue(@NonNull String settingKey, @NonNull Integer originalValue,
            @NonNull TypedXmlSerializer serializer) throws IOException {
        serializer.text(Integer.toString(originalValue));
    }

    @Override
    public Integer deserializeOriginalValue(@NonNull TypedXmlPullParser parser,
            @NonNull String settingKey) throws IOException, XmlPullParserException {
        return Integer.parseInt(parser.nextText());
    }

    /**
     * Sets whether to skip security features for test.
     *
     * @param skipSecurityFeaturesForTest Whether to skip security features for test.
     */
    void setSkipSecurityFeaturesForTest(boolean skipSecurityFeaturesForTest) {
        mSkipSecurityFeaturesForTest = skipSecurityFeaturesForTest;
    }
}
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.security.authenticationpolicy.settings;

import android.util.Log;
import android.util.Slog;

import androidx.annotation.NonNull;

import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

/**
 * Stores the expected state of a setting during secure lock device, as well as the original state
 * of the setting at the time secure lock device is enabled, in order to restore to this state upon
 * secure lock device being disabled.
 *
 * @param <T> The type of the setting.
 */
public class ManagedSetting<T> {
    private static final String TAG = "ManagedSetting";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final String XML_TAG_SETTING_ORIGINAL_VALUE = "setting-original-value";
    private final SettingState<T> mState;
    private final SettingController<T> mController;

    ManagedSetting(@NonNull SettingState<T> state, @NonNull SettingController<T> controller) {
        mState = state;
        mController = controller;
    }

    /**
     * Stores the original value of the setting in [SettingState.originalValue]
     * @param userId The user id to store the setting value for.
     */
    public void storeOriginalValue(int userId) {
        try {
            mController.storeOriginalValue(mState, userId);
            if (DEBUG) {
                Slog.d(TAG, "Stored original value to SettingState " + mState + " for user "
                        + userId);
            }
        } catch (Exception e) {
            Slog.e(TAG, "Failed to store original value to SettingState " + mState + " for user "
                    + userId, e);
        }
    }

    /**
     * Applies the expected setting value from [SettingState.secureLockDeviceValue]
     *
     * @param userId The user id to apply the setting value to.
     */
    public void applySecureLockDeviceValue(int userId) {
        try {
            mController.applySecureLockDeviceValue(mState, userId);
            if (DEBUG) {
                Slog.d(TAG, "Applied secure lock device value to SettingState " + mState
                        + " for user " + userId);
            }
        } catch (Exception e) {
            Slog.d(TAG, "Failed to apply secure lock device value to SettingState " + mState
                    + " for user " + userId, e);
        }
    }

    /**
     * Retrieves the original value of the setting with [SettingState.originalValue] and restores
     * the
     * setting to this state.
     *
     * @param userId The user id to apply the setting value to.
     */
    public void restoreFromOriginalValue(int userId) {
        try {
            mController.restoreFromOriginalValue(mState, userId);
            if (DEBUG) {
                Slog.d(TAG, "SettingState " + mState + " restored to original value before "
                        + "secure lock device was enabled by user " + userId);
            }
        } catch (Exception e) {
            Slog.e(TAG, "Failed to restore original value " + mState.getOriginalValue()
                    + " on setting with key " + mState.getSettingKey() + " using controller "
                    + mController.getClass().getSimpleName(), e);
        }
    }

    /**
     * Serializes original value using {@code serializer}.
     *
     * @param serializer The serializer used to serialize the original value.
     */
    public void serializeOriginalValue(@NonNull TypedXmlSerializer serializer) {
        String settingKey = mState.getSettingKey();
        T originalValue = mState.getOriginalValue();
        if (originalValue == null) {
            Slog.w(TAG, "Original value is null for setting with key " + settingKey);
            return;
        }
        Slog.w(TAG, "Serializing original value for setting with key " + settingKey);
        try {
            serializer.startTag(null, XML_TAG_SETTING_ORIGINAL_VALUE);
            mController.serializeOriginalValue(settingKey, originalValue, serializer);
            serializer.endTag(null, XML_TAG_SETTING_ORIGINAL_VALUE);
        } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Error serializing original value for setting with key: "
                    + settingKey, e);
        }
    }

    /**
     * Deserializes the original value from the XML and sets {@code SettingState.originalValue} to
     * the retrieved value.
     * @param parser The XML parser.
     * @param key The key of the setting being deserialized.
     */
    public void deserializeAndRestoreOriginalValueFromXml(TypedXmlPullParser parser, String key) {
        try {
            T originalValue = mController.deserializeOriginalValue(parser, key);
            mState.setOriginalValue(originalValue);
            if (DEBUG) {
                Slog.d(TAG, "Deserialized original value for setting " + this);
            }
        } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Error deserializing original value for setting " + this, e);
        }
    }

    /** String identifier for the [SettingState] */
    @NonNull
    public String getSettingKey() {
        return mState.getSettingKey();
    }

    /** [SettingState.SettingType] of the [SettingState] */
    @NonNull
    public SettingState.SettingType getSettingType() {
        return mState.getSettingType();
    }

    @Override
    public String toString() {
        return "[Key " + mState.getSettingKey() + ", Type " + mState.getSettingType()
                + ", Original value " + mState.getOriginalValue() + ", Secure lock device value "
                + mState.getSecureLockDeviceValue() + "]";
    }
}
Loading