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

Commit 1f5b4ef5 authored by David Anderson's avatar David Anderson Committed by Android (Google) Code Review
Browse files

Merge "Partition weaver slots to avoid conflicts between the host image and GSI."

parents b266f38e 28dea685
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -409,7 +409,8 @@ public class LockSettingsService extends ILockSettings.Stub {
        }

        public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) {
            return new SyntheticPasswordManager(getContext(), storage, getUserManager());
            return new SyntheticPasswordManager(getContext(), storage, getUserManager(),
                    new PasswordSlotManager());
        }

        public boolean hasBiometrics() {
+191 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.locksettings;

import android.os.SystemProperties;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * A class that maintains a mapping of which password slots are used by alternate OS images when
 * dual-booting a device. Currently, slots can either be owned by the host OS or a live GSI.
 * This mapping is stored in /metadata/password_slots/slot_map using Java Properties.
 *
 * If a /metadata partition does not exist, GSIs are not supported, and PasswordSlotManager will
 * simply not persist the slot mapping.
 */
public class PasswordSlotManager {
    private static final String TAG = "PasswordSlotManager";

    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
    private static final String SLOT_MAP_DIR = "/metadata/password_slots";

    // This maps each used password slot to the OS image that created it. Password slots are
    // integer keys/indices into secure storage. The OS image is recorded as a string. The factory
    // image is "host" and GSIs are "gsi<N>" where N >= 1.
    private final Map<Integer, String> mSlotMap;

    public PasswordSlotManager() {
        mSlotMap = loadSlotMap();
    }

    @VisibleForTesting
    protected String getSlotMapDir() {
        return SLOT_MAP_DIR;
    }

    @VisibleForTesting
    protected int getGsiImageNumber() {
        return SystemProperties.getInt(GSI_RUNNING_PROP, 0);
    }

    /**
     * Notify the manager of which slots are definitively in use by the current OS image.
     *
     * @throws RuntimeException
     */
    public void refreshActiveSlots(Set<Integer> activeSlots) throws RuntimeException {
        // Update which slots are owned by the current image.
        final HashSet<Integer> slotsToDelete = new HashSet<Integer>();
        for (Map.Entry<Integer, String> entry : mSlotMap.entrySet()) {
            // Delete possibly stale entries for the current image.
            if (entry.getValue().equals(getMode())) {
                slotsToDelete.add(entry.getKey());
            }
        }
        for (Integer slot : slotsToDelete) {
            mSlotMap.remove(slot);
        }

        // Add slots for the current image.
        for (Integer slot : activeSlots) {
            mSlotMap.put(slot, getMode());
        }

        saveSlotMap();
    }

    /**
     * Mark the given slot as in use by the current OS image.
     *
     * @throws RuntimeException
     */
    public void markSlotInUse(int slot) throws RuntimeException {
        if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
            throw new RuntimeException("password slot " + slot + " is not available");
        }
        mSlotMap.put(slot, getMode());
        saveSlotMap();
    }

    /**
     * Mark the given slot as no longer in use by the current OS image.
     *
     * @throws RuntimeException
     */
    public void markSlotDeleted(int slot) throws RuntimeException {
        if (mSlotMap.containsKey(slot) && mSlotMap.get(slot) != getMode()) {
            throw new RuntimeException("password slot " + slot + " cannot be deleted");
        }
        mSlotMap.remove(slot);
        saveSlotMap();
    }

    /**
     * Return the set of slots used across all OS images.
     *
     * @return Integer set of all used slots.
     */
    public Set<Integer> getUsedSlots() {
        return Collections.unmodifiableSet(mSlotMap.keySet());
    }

    private File getSlotMapFile() {
        return Paths.get(getSlotMapDir(), "slot_map").toFile();
    }

    private String getMode() {
        int gsiIndex = getGsiImageNumber();
        if (gsiIndex > 0) {
            return "gsi" + gsiIndex;
        }
        return "host";
    }

    @VisibleForTesting
    protected Map<Integer, String> loadSlotMap(InputStream stream) throws IOException {
        final HashMap<Integer, String> map = new HashMap<Integer, String>();
        final Properties props = new Properties();
        props.load(stream);
        for (String slotString : props.stringPropertyNames()) {
            final int slot = Integer.parseInt(slotString);
            final String owner = props.getProperty(slotString);
            map.put(slot, owner);
        }
        return map;
    }

    private Map<Integer, String> loadSlotMap() {
        // It's okay if the file doesn't exist.
        final File file = getSlotMapFile();
        if (file.exists()) {
            try (FileInputStream stream = new FileInputStream(file)) {
                return loadSlotMap(stream);
            } catch (Exception e) {
                Slog.e(TAG, "Could not load slot map file", e);
            }
        }
        return new HashMap<Integer, String>();
    }

    @VisibleForTesting
    protected void saveSlotMap(OutputStream stream) throws IOException {
        final Properties props = new Properties();
        for (Map.Entry<Integer, String> entry : mSlotMap.entrySet()) {
            props.setProperty(entry.getKey().toString(), entry.getValue());
        }
        props.store(stream, "");
    }

    private void saveSlotMap() {
        if (!getSlotMapFile().getParentFile().exists()) {
            Slog.w(TAG, "Not saving slot map, " + getSlotMapDir() + " does not exist");
            return;
        }

        try (FileOutputStream fos = new FileOutputStream(getSlotMapFile())) {
            saveSlotMap(fos);
        } catch (IOException e) {
            Slog.e(TAG, "failed to save password slot map", e);
        }
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -287,14 +287,16 @@ public class SyntheticPasswordManager {
    private LockSettingsStorage mStorage;
    private IWeaver mWeaver;
    private WeaverConfig mWeaverConfig;
    private PasswordSlotManager mPasswordSlotManager;

    private final UserManager mUserManager;

    public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
            UserManager userManager) {
            UserManager userManager, PasswordSlotManager passwordSlotManager) {
        mContext = context;
        mStorage = storage;
        mUserManager = userManager;
        mPasswordSlotManager = passwordSlotManager;
    }

    @VisibleForTesting
@@ -324,6 +326,7 @@ public class SyntheticPasswordManager {
                        mWeaver = null;
                    }
                });
                mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to get weaver service", e);
@@ -561,6 +564,7 @@ public class SyntheticPasswordManager {
                Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
                try {
                    weaverEnroll(slot, null, null);
                    mPasswordSlotManager.markSlotDeleted(slot);
                } catch (RemoteException e) {
                    Log.w(TAG, "Failed to destroy slot", e);
                }
@@ -595,6 +599,7 @@ public class SyntheticPasswordManager {

    private int getNextAvailableWeaverSlot() {
        Set<Integer> usedSlots = getUsedWeaverSlots();
        usedSlots.addAll(mPasswordSlotManager.getUsedSlots());
        for (int i = 0; i < mWeaverConfig.slots; i++) {
            if (!usedSlots.contains(i)) {
                return i;
@@ -640,6 +645,7 @@ public class SyntheticPasswordManager {
                return DEFAULT_HANDLE;
            }
            saveWeaverSlot(weaverSlot, handle, userId);
            mPasswordSlotManager.markSlotInUse(weaverSlot);
            synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);

            pwd.passwordHandle = null;
@@ -798,6 +804,7 @@ public class SyntheticPasswordManager {
                return false;
            }
            saveWeaverSlot(slot, handle, userId);
            mPasswordSlotManager.markSlotInUse(slot);
        }
        saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
+5 −1
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
    IAuthSecret mAuthSecretService;
    WindowManagerInternal mMockWindowManager;
    FakeGsiService mGsiService;
    PasswordSlotManagerTestable mPasswordSlotManager;
    protected boolean mHasSecureLockScreen;

    @Override
@@ -103,6 +104,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
        mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
        mMockWindowManager = mock(WindowManagerInternal.class);
        mGsiService = new FakeGsiService();
        mPasswordSlotManager = new PasswordSlotManagerTestable();

        LocalServices.removeServiceForTest(LockSettingsInternal.class);
        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -135,7 +137,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
            }
        };
        mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
                mUserManager);
                mUserManager, mPasswordSlotManager);
        mAuthSecretService = mock(IAuthSecret.class);
        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
                mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
@@ -223,6 +225,8 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {

        File storageDir = mStorage.mStorageDir;
        assertTrue(FileUtils.deleteContents(storageDir));

        mPasswordSlotManager.cleanup();
    }

    protected void assertNotEquals(long expected, long actual) {
+4 −3
Original line number Diff line number Diff line
@@ -35,10 +35,12 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {

    private FakeGateKeeperService mGateKeeper;
    private IWeaver mWeaverService;
    private PasswordSlotManagerTestable mPasswordSlotManager;

    public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
            FakeGateKeeperService gatekeeper, UserManager userManager) {
        super(context, storage, userManager);
            FakeGateKeeperService gatekeeper, UserManager userManager,
            PasswordSlotManager passwordSlotManager) {
        super(context, storage, userManager, passwordSlotManager);
        mGateKeeper = gatekeeper;
    }

@@ -117,5 +119,4 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
        mWeaverService = new MockWeaverService();
        initWeaverService();
    }

}
Loading