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

Commit 2f5d6ec1 authored by Matt Pietal's avatar Matt Pietal
Browse files

Add supervised user support

Supports adding a supervised user when configured. Modify the
UserSwitcherController so that all supports dialogs will get the new
"Add user" option if available.

Fixes: 218310361
Test: atest UserSwitcherControllerTest KeyguardUserSwitcherAdapterTest UserDetailViewAdapterTest KeyguardSecurityContainerTest
Change-Id: Ib79dcde2c726cc9508aef667b43662152d047137
parent 4ff2a4e6
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
<!--
  ~ Copyright (C) 2022 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.
  -->
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@*android:drawable/ic_add_supervised_user" />
</layer-list>
+2 −0
Original line number Diff line number Diff line
@@ -2402,4 +2402,6 @@

    <!-- Generic "add" string [CHAR LIMIT=NONE] -->
    <string name="add">Add</string>
    <!-- Add supervised user -->
    <string name="add_user_supervised" translatable="false">@*android:string/supervised_user_creation_label</string>
</resources>
+86 −26
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -159,6 +160,7 @@ public class UserSwitcherController implements Dumpable {
    private final AtomicBoolean mGuestCreationScheduled;
    private FalsingManager mFalsingManager;
    private View mView;
    private String mCreateSupervisedUserPackage;

    @Inject
    public UserSwitcherController(Context context,
@@ -255,6 +257,9 @@ public class UserSwitcherController implements Dumpable {
        keyguardStateController.addCallback(mCallback);
        listenForCallState();

        mCreateSupervisedUserPackage = mContext.getString(
                com.android.internal.R.string.config_supervisedUserCreationPackage);

        dumpManager.registerDumpable(getClass().getSimpleName(), this);

        refreshUsers(UserHandle.USER_NULL);
@@ -307,14 +312,10 @@ public class UserSwitcherController implements Dumpable {
                // User 0
                boolean canSwitchUsers = mUserManager.getUserSwitchability(
                        UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
                UserInfo currentUserInfo = null;
                UserRecord guestRecord = null;

                for (UserInfo info : infos) {
                    boolean isCurrent = currentId == info.id;
                    if (isCurrent) {
                        currentUserInfo = info;
                    }
                    boolean switchToEnabled = canSwitchUsers || isCurrent;
                    if (info.isEnabled()) {
                        if (info.isGuest()) {
@@ -322,7 +323,8 @@ public class UserSwitcherController implements Dumpable {
                            // the icon shouldn't be enabled even if the user is current
                            guestRecord = new UserRecord(info, null /* picture */,
                                    true /* isGuest */, isCurrent, false /* isAddUser */,
                                    false /* isRestricted */, canSwitchUsers);
                                    false /* isRestricted */, canSwitchUsers,
                                    false /* isAddSupervisedUser */);
                        } else if (info.supportsSwitchToByUser()) {
                            Bitmap picture = bitmaps.get(info.id);
                            if (picture == null) {
@@ -337,7 +339,7 @@ public class UserSwitcherController implements Dumpable {
                            }
                            records.add(new UserRecord(info, picture, false /* isGuest */,
                                    isCurrent, false /* isAddUser */, false /* isRestricted */,
                                    switchToEnabled));
                                    switchToEnabled, false /* isAddSupervisedUser */));
                        }
                    }
                }
@@ -345,19 +347,6 @@ public class UserSwitcherController implements Dumpable {
                    Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
                }

                boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
                                UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
                boolean currentUserCanCreateUsers = currentUserInfo != null
                        && (currentUserInfo.isAdmin()
                                || currentUserInfo.id == UserHandle.USER_SYSTEM)
                        && systemCanCreateUsers;
                boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
                boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
                        && guestRecord == null;
                boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
                        && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
                boolean createIsRestricted = !addUsersWhenLocked;

                if (guestRecord == null) {
                    if (mGuestUserAutoCreated) {
                        // If mGuestIsResetting=true, the switch should be disabled since
@@ -368,13 +357,14 @@ public class UserSwitcherController implements Dumpable {
                        guestRecord = new UserRecord(null /* info */, null /* picture */,
                                true /* isGuest */, false /* isCurrent */,
                                false /* isAddUser */, false /* isRestricted */,
                                isSwitchToGuestEnabled);
                                isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
                        records.add(guestRecord);
                    } else if (canCreateGuest) {
                    } else if (canCreateGuest(guestRecord != null)) {
                        guestRecord = new UserRecord(null /* info */, null /* picture */,
                                true /* isGuest */, false /* isCurrent */,
                                false /* isAddUser */, createIsRestricted, canSwitchUsers);
                                false /* isAddUser */, createIsRestricted(), canSwitchUsers,
                                false /* isAddSupervisedUser */);
                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
                        records.add(guestRecord);
                    }
@@ -382,10 +372,19 @@ public class UserSwitcherController implements Dumpable {
                    records.add(guestRecord);
                }

                if (canCreateUser) {
                if (canCreateUser()) {
                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
                            false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
                            createIsRestricted, canSwitchUsers);
                            createIsRestricted(), canSwitchUsers,
                            false /* isAddSupervisedUser */);
                    checkIfAddUserDisallowedByAdminOnly(addUserRecord);
                    records.add(addUserRecord);
                }

                if (canCreateSupervisedUser()) {
                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
                            false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
                            createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
                    checkIfAddUserDisallowedByAdminOnly(addUserRecord);
                    records.add(addUserRecord);
                }
@@ -403,6 +402,40 @@ public class UserSwitcherController implements Dumpable {
        }.execute((SparseArray) bitmaps);
    }

    boolean systemCanCreateUsers() {
        return !mUserManager.hasBaseUserRestriction(
                UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
    }

    boolean currentUserCanCreateUsers() {
        UserInfo currentUser = mUserTracker.getUserInfo();
        return currentUser != null
                && (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM)
                && systemCanCreateUsers();
    }

    boolean anyoneCanCreateUsers() {
        return systemCanCreateUsers() && mAddUsersFromLockScreen;
    }

    boolean canCreateGuest(boolean hasExistingGuest) {
        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                && !hasExistingGuest;
    }

    boolean canCreateUser() {
        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
    }

    boolean createIsRestricted() {
        return mAddUsersFromLockScreen;
    }

    boolean canCreateSupervisedUser() {
        return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser();
    }

    private void pauseRefreshUsers() {
        if (!mPauseRefreshUsers) {
            mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
@@ -485,6 +518,9 @@ public class UserSwitcherController implements Dumpable {
        } else if (record.isAddUser) {
            showAddUserDialog(dialogShower);
            return;
        } else if (record.isAddSupervisedUser) {
            startSupervisedUserActivity();
            return;
        } else {
            id = record.info.id;
        }
@@ -561,6 +597,22 @@ public class UserSwitcherController implements Dumpable {
        }
    }

    private void startSupervisedUserActivity() {
        final Intent intent = new Intent()
                .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
                .setPackage(mCreateSupervisedUserPackage)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        // TODO(b/209659998): [to-be-removed] fallback activity for supervised user creation.
        if (mContext.getPackageManager().resolveActivity(intent, 0) == null) {
            intent.setPackage(null)
                    .setClassName("com.android.settings",
                        "com.android.settings.users.AddSupervisedUserActivity");
        }

        mContext.startActivity(intent);
    }

    private void listenForCallState() {
        mTelephonyListenerManager.addCallStateListener(mPhoneStateListener);
    }
@@ -941,6 +993,8 @@ public class UserSwitcherController implements Dumpable {
                }
            } else if (item.isAddUser) {
                return context.getString(R.string.user_add_user);
            } else if (item.isAddSupervisedUser) {
                return context.getString(R.string.add_user_supervised);
            } else {
                return item.info.name;
            }
@@ -958,6 +1012,8 @@ public class UserSwitcherController implements Dumpable {
                iconRes = R.drawable.ic_add_circle;
            } else if (item.isGuest) {
                iconRes = R.drawable.ic_avatar_guest_user;
            } else if (item.isAddSupervisedUser) {
                iconRes = R.drawable.ic_add_supervised_user;
            } else {
                iconRes = R.drawable.ic_avatar_user;
            }
@@ -1000,6 +1056,7 @@ public class UserSwitcherController implements Dumpable {
        public final boolean isGuest;
        public final boolean isCurrent;
        public final boolean isAddUser;
        public final boolean isAddSupervisedUser;
        /** If true, the record is only visible to the owner and only when unlocked. */
        public final boolean isRestricted;
        public boolean isDisabledByAdmin;
@@ -1007,7 +1064,8 @@ public class UserSwitcherController implements Dumpable {
        public boolean isSwitchToEnabled;

        public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
                boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
                boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled,
                boolean isAddSupervisedUser) {
            this.info = info;
            this.picture = picture;
            this.isGuest = isGuest;
@@ -1015,11 +1073,12 @@ public class UserSwitcherController implements Dumpable {
            this.isAddUser = isAddUser;
            this.isRestricted = isRestricted;
            this.isSwitchToEnabled = isSwitchToEnabled;
            this.isAddSupervisedUser = isAddSupervisedUser;
        }

        public UserRecord copyWithIsCurrent(boolean _isCurrent) {
            return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
                    isSwitchToEnabled);
                    isSwitchToEnabled, isAddSupervisedUser);
        }

        public int resolveId() {
@@ -1043,6 +1102,7 @@ public class UserSwitcherController implements Dumpable {
            }
            if (isGuest) sb.append(" <isGuest>");
            if (isAddUser) sb.append(" <isAddUser>");
            if (isAddSupervisedUser) sb.append(" <isAddSupervisedUser>");
            if (isCurrent) sb.append(" <isCurrent>");
            if (picture != null) sb.append(" <hasPicture>");
            if (isRestricted) sb.append(" <isRestricted>");
+7 −9
Original line number Diff line number Diff line
@@ -71,8 +71,7 @@ class UserSwitcherActivity @Inject constructor(
    private lateinit var broadcastReceiver: BroadcastReceiver
    private var popupMenu: UserSwitcherPopupMenu? = null
    private lateinit var addButton: View
    private var addUserItem: UserRecord? = null
    private var addGuestItem: UserRecord? = null
    private var addUserRecords = mutableListOf<UserRecord>()

    private val adapter = object : BaseUserAdapter(userSwitcherController) {
        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
@@ -203,8 +202,7 @@ class UserSwitcherActivity @Inject constructor(

    private fun showPopupMenu() {
        val items = mutableListOf<UserRecord>()
        addUserItem?.let { items.add(it) }
        addGuestItem?.let { items.add(it) }
        addUserRecords.forEach { items.add(it) }

        var popupMenuAdapter = ItemAdapter(
            this,
@@ -249,10 +247,10 @@ class UserSwitcherActivity @Inject constructor(
        val flow = requireViewById<Flow>(R.id.flow)
        for (i in 0 until adapter.getCount()) {
            val item = adapter.getItem(i)
            if (item.isAddUser) {
                addUserItem = item
            } else if (item.isGuest && item.info == null) {
                addGuestItem = item
            if (item.isAddUser ||
                item.isAddSupervisedUser ||
                item.isGuest && item.info == null) {
                addUserRecords.add(item)
            } else {
                val userView = adapter.getView(i, null, parent)
                userView.setId(View.generateViewId())
@@ -273,7 +271,7 @@ class UserSwitcherActivity @Inject constructor(
            }
        }

        if (addUserItem != null || addGuestItem != null) {
        if (!addUserRecords.isEmpty()) {
            addButton.visibility = View.VISIBLE
        } else {
            addButton.visibility = View.GONE
+2 −1
Original line number Diff line number Diff line
@@ -307,7 +307,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
            UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */,
                    0 /* flags */);
            users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
                    false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */));
                    false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */,
                    false /* isAddSupervisedUser */));
        }
        return users;
    }
Loading