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

Commit bce7071f authored by Jason Parks's avatar Jason Parks Committed by Android (Google) Code Review
Browse files

Merge "Add the ability to enable and disable supervision" into main

parents 72e54636 ae718856
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -21,5 +21,5 @@ package android.app.supervision;
 * {@hide}
 */
interface ISupervisionManager {
    boolean isSupervisionEnabled();
    boolean isSupervisionEnabledForUser(int userId);
}
+3 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.app.supervision;

import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.RemoteException;
@@ -45,13 +46,12 @@ public class SupervisionManager {
     *
     * @hide
     */
    @UserHandleAware
    public boolean isSupervisionEnabled() {
        try {
            return mService.isSupervisionEnabled();
            return mService.isSupervisionEnabledForUser(mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }


}
+46 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.supervision;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.Bundle;

/**
 * Local system service interface for {@link SupervisionService}.
 *
 * @hide Only for use within Android OS.
 */
public abstract class SupervisionManagerInternal {
    /**
     * Returns whether supervision is enabled for the specified user
     *
     * @param userId The user to retrieve the supervision state for
     * @return whether the user is supervised
     */
    public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);

    /**
     * Sets whether the supervision lock screen should be shown for the specified user
     *
     * @param userId The user set the superivision state for
     * @param enabled Whether or not the superivision lock screen needs to be shown
     * @param options Optional configuration parameters for the supervision lock screen
     */
    public abstract void setSupervisionLockscreenEnabledForUser(
            @UserIdInt int userId, boolean enabled, @Nullable Bundle options);
}
+88 −7
Original line number Diff line number Diff line
@@ -18,14 +18,22 @@ package com.android.server.supervision;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.supervision.ISupervisionManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -38,13 +46,25 @@ public class SupervisionService extends ISupervisionManager.Stub {

    private final Context mContext;

    // TODO(b/362756788): Does this need to be a LockGuard lock?
    private final Object mLockDoNoUseDirectly = new Object();

    @GuardedBy("getLockObject()")
    private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();

    private final UserManagerInternal mUserManagerInternal;

    public SupervisionService(Context context) {
        mContext = context.createAttributionContext("SupervisionService");
        mContext = context.createAttributionContext(LOG_TAG);
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
        mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
    }

    @Override
    public boolean isSupervisionEnabled() {
        return false;
    public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
        synchronized (getLockObject()) {
            return getUserDataLocked(userId).supervisionEnabled;
        }
    }

    @Override
@@ -60,11 +80,44 @@ public class SupervisionService extends ISupervisionManager.Stub {
    }

    @Override
    protected void dump(
            @NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
    protected void dump(@NonNull FileDescriptor fd,
            @NonNull PrintWriter printWriter, @Nullable String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, printWriter)) return;

        try (var pw = new IndentingPrintWriter(printWriter, "  ")) {
            pw.println("SupervisionService state:");
            pw.increaseIndent();

            var users = mUserManagerInternal.getUsers(false);
            synchronized (getLockObject()) {
                for (var user : users) {
                    getUserDataLocked(user.id).dump(pw);
                    pw.println();
                }
            }
        }
    }

    private Object getLockObject() {
        return mLockDoNoUseDirectly;
    }

    @NonNull
    @GuardedBy("getLockObject()")
    SupervisionUserData getUserDataLocked(@UserIdInt int userId) {
        SupervisionUserData data = mUserData.get(userId);
        if (data == null) {
            // TODO(b/362790738): Do not create user data for nonexistent users.
            data = new SupervisionUserData(userId);
            mUserData.append(userId, data);
        }
        return data;
    }

        fout.println("Supervision enabled: " + isSupervisionEnabled());
    void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
        synchronized (getLockObject()) {
            getUserDataLocked(userId).supervisionEnabled = enabled;
        }
    }

    public static class Lifecycle extends SystemService {
@@ -77,7 +130,35 @@ public class SupervisionService extends ISupervisionManager.Stub {

        @Override
        public void onStart() {
            publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal);
            publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
        }
    }

    final SupervisionManagerInternal mInternal = new SupervisionManagerInternal() {
        public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
            synchronized (getLockObject()) {
                return getUserDataLocked(userId).supervisionEnabled;
            }
        }

        @Override
        public void setSupervisionLockscreenEnabledForUser(
                @UserIdInt int userId, boolean enabled, @Nullable Bundle options) {
            synchronized (getLockObject()) {
                SupervisionUserData data = getUserDataLocked(userId);
                data.supervisionLockScreenEnabled = enabled;
                data.supervisionLockScreenOptions = options;
            }
        }
    };

    private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
        @Override
        public void onUserRemoved(UserInfo user) {
            synchronized (getLockObject()) {
                mUserData.remove(user.id);
            }
        }
    }
}
+15 −17
Original line number Diff line number Diff line
@@ -17,8 +17,7 @@
package com.android.server.supervision;

import android.os.ShellCommand;

import java.io.PrintWriter;
import android.os.UserHandle;

public class SupervisionServiceShellCommand extends ShellCommand {
    private final SupervisionService mService;
@@ -32,30 +31,29 @@ public class SupervisionServiceShellCommand extends ShellCommand {
        if (cmd == null) {
            return handleDefaultCommands(null);
        }
        final PrintWriter pw = getOutPrintWriter();
        switch (cmd) {
            case "help": return help(pw);
            case "is-enabled": return isEnabled(pw);
            case "enable": return setEnabled(true);
            case "disable": return setEnabled(false);
            default: return handleDefaultCommands(cmd);
        }
    }

    private int help(PrintWriter pw) {
        pw.println("Supervision service commands:");
        pw.println("  help");
        pw.println("      Prints this help text");
        pw.println("  is-enabled");
        pw.println("      Is supervision enabled");
        return 0;
    }

    private int isEnabled(PrintWriter pw) {
        pw.println(mService.isSupervisionEnabled());
    private int setEnabled(boolean enabled) {
        final var pw = getOutPrintWriter();
        final var userId = UserHandle.parseUserArg(getNextArgRequired());
        mService.setSupervisionEnabledForUser(userId, enabled);
        return 0;
    }

    @Override
    public void onHelp() {
        help(getOutPrintWriter());
        final var pw = getOutPrintWriter();
        pw.println("Supervision service (supervision) commands:");
        pw.println("  help");
        pw.println("      Prints this help text");
        pw.println("  enable <USER_ID>");
        pw.println("      Enables supervision for the given user.");
        pw.println("  disable <USER_ID>");
        pw.println("      Disables supervision for the given user.");
    }
}
Loading