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

Commit 4a203e01 authored by Sandy Pan's avatar Sandy Pan Committed by Android (Google) Code Review
Browse files

Merge "Add supervision recovery email and id read/write APIs in Supervision Service" into main

parents 8f71471c fef1fa1f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.app.supervision;

import android.content.Intent;
import android.app.supervision.SupervisionRecoveryInfo;

/**
 * Internal IPC interface to the supervision service.
@@ -29,4 +30,6 @@ interface ISupervisionManager {
    String getActiveSupervisionAppPackage(int userId);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)")
    boolean shouldAllowBypassingSupervisionRoleQualification();
    oneway void setSupervisionRecoveryInfo(in SupervisionRecoveryInfo recoveryInfo);
    SupervisionRecoveryInfo getSupervisionRecoveryInfo();
}
+33 −3
Original line number Diff line number Diff line
@@ -196,11 +196,9 @@ public class SupervisionManager {
        return null;
    }


    /**
     * @return {@code true} if bypassing the qualification is allowed for the specified role based
     *     on the current state of the device.
     *
     * @hide
     */
    @SystemApi
@@ -216,4 +214,36 @@ public class SupervisionManager {
        }
        return false;
    }

    /**
     * Sets the supervision recovery information.
     *
     * @hide
     */
    public void setSupervisionRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
        if (mService != null) {
            try {
                mService.setSupervisionRecoveryInfo(recoveryInfo);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Returns the supervision recovery information or null if recovery is not setup.
     *
     * @hide
     */
    @Nullable
    public SupervisionRecoveryInfo getSupervisionRecoveryInfo() {
        if (mService != null) {
            try {
                return mService.getSupervisionRecoveryInfo();
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return null;
    }
}
+31 −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 android.app.supervision;

/**
 * A parcelable of the supervision recovery information. This stores information for recovery
 * purposes.
 *
 * <p>Email: The email for recovery. ID: The account id for recovery.
 *
 * @hide
 */
@JavaDerive(equals = true, toString = true)
parcelable SupervisionRecoveryInfo {
    @nullable String email;
    @nullable String id;
}
+114 −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.supervision;

import android.app.supervision.SupervisionRecoveryInfo;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.util.Log;

import java.io.File;

/**
 * Provides storage and retrieval of device supervision recovery information.
 *
 * <p>This class uses {@link SharedPreferences} as a temporary solution for persistent storage of
 * the recovery email and ID associated with device supervision.
 *
 * <p>The storage is managed as a singleton, ensuring a single point of access for recovery
 * information. Access to the shared preferences is synchronized to ensure thread safety.
 *
 * <p>TODO(b/406054267): need to figure out better solutions(binary xml) for persistent storage.
 */
public class SupervisionRecoveryInfoStorage {
    private static final String LOG_TAG = "RecoveryInfoStorage";
    private static final String PREF_NAME = "supervision_recovery_info";
    private static final String KEY_EMAIL = "email";
    private static final String KEY_ID = "id";

    private final SharedPreferences mSharedPreferences;

    private static SupervisionRecoveryInfoStorage sInstance;

    private static final Object sLock = new Object();

    private SupervisionRecoveryInfoStorage(Context context) {
        Context deviceContext = context.createDeviceProtectedStorageContext();
        File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREF_NAME);
        mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs, Context.MODE_PRIVATE);
    }

    /**
     * Gets the singleton instance of {@link SupervisionRecoveryInfoStorage}.
     *
     * @param context The application context.
     * @return The singleton instance.
     */
    public static SupervisionRecoveryInfoStorage getInstance(Context context) {
        synchronized (sLock) {
            if (sInstance == null) {
                sInstance = new SupervisionRecoveryInfoStorage(context.getApplicationContext());
            }
            return sInstance;
        }
    }

    /**
     * Loads the device supervision recovery information from persistent storage.
     *
     * @return The {@link SupervisionRecoveryInfo} if found, otherwise {@code null}.
     */
    public SupervisionRecoveryInfo loadRecoveryInfo() {
        synchronized (sLock) {
            String email = mSharedPreferences.getString(KEY_EMAIL, null);
            String id = mSharedPreferences.getString(KEY_ID, null);

            if (email != null || id != null) {
                SupervisionRecoveryInfo info = new SupervisionRecoveryInfo();
                info.email = email;
                info.id = id;
                return info;
            }
        }
        return null;
    }

    /**
     * Saves the device supervision recovery information to persistent storage.
     *
     * @param recoveryInfo The {@link SupervisionRecoveryInfo} to save or {@code null} to clear the
     *     stored information.
     */
    public void saveRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
        synchronized (sLock) {
            SharedPreferences.Editor editor = mSharedPreferences.edit();

            if (recoveryInfo == null) {
                editor.remove(KEY_EMAIL);
                editor.remove(KEY_ID);
            } else {
                editor.putString(KEY_EMAIL, recoveryInfo.email);
                editor.putString(KEY_ID, recoveryInfo.id);
            }
            editor.apply();
            if (!editor.commit()) {
                Log.e(LOG_TAG, "Failed to save recovery info to SharedPreferences");
            }
        }
    }
}
+14 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
import android.app.supervision.SupervisionManagerInternal;
import android.app.supervision.SupervisionRecoveryInfo;
import android.app.supervision.flags.Flags;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -174,6 +175,18 @@ public class SupervisionService extends ISupervisionManager.Stub {
        return intent;
    }

    /** Set the Supervision Recovery Info. */
    @Override
    public void setSupervisionRecoveryInfo(SupervisionRecoveryInfo recoveryInfo) {
        SupervisionRecoveryInfoStorage.getInstance(mContext).saveRecoveryInfo(recoveryInfo);
    }

    /** Returns the Supervision Recovery Info or null if recovery is not set. */
    @Override
    public SupervisionRecoveryInfo getSupervisionRecoveryInfo() {
        return SupervisionRecoveryInfoStorage.getInstance(mContext).loadRecoveryInfo();
    }

    @Override
    public boolean shouldAllowBypassingSupervisionRoleQualification() {
        enforcePermission(MANAGE_ROLE_HOLDERS);
@@ -196,7 +209,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
    /**
     * Returns true if there are any non-default non-test users.
     *
     * This excludes the system and main user(s) as those users are created by default.
     * <p>This excludes the system and main user(s) as those users are created by default.
     */
    private boolean hasNonTestDefaultUsers() {
        List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(true);
Loading