Zarządzanie wieloma użytkownikami

Ten przewodnik dla programistów wyjaśnia, jak kontroler zasad dotyczących urządzeń (DPC) może zarządzać wieloma użytkownikami Androida na specjalnych urządzeniach.

Przegląd

Twoja karta DPC może pomóc wielu osobom wspólnie korzystać z jednego dedykowanego urządzenia. DPC działający na w pełni zarządzanym urządzeniu może tworzyć 2 typy użytkowników i nimi zarządzać:

  • Użytkownicy pomocniczy to użytkownicy Androida, którzy między sesjami mają zapisane osobne aplikacje i dane. Do zarządzania użytkownikami służy komponent administratora. Tacy użytkownicy są przydatni w przypadkach, gdy urządzenie jest odbierane na początku zmiany, na przykład kierowcy lub pracownicy ochrony.
  • Użytkownicy tymczasowy to użytkownicy pomocniczy, których system usuwa, gdy użytkownik zatrzymuje działanie, przełączasz się lub uruchamia ponownie. Tacy użytkownicy są przydatni w sytuacjach, gdy dane mogą zostać usunięte po zakończeniu sesji, np. w kioskach internetowych dostępnych publicznie.

Do zarządzania dedykowanym urządzeniem i dodatkowymi użytkownikami możesz używać swojej obecnej karty DPC. Składnik administracyjny w DPC ustawia siebie jako administratora nowych użytkowników dodatkowych podczas ich tworzenia.

Główny użytkownik i dwóch użytkowników pomocniczych.
Rysunek 1. Główne i dodatkowe konta użytkowników zarządzane przez administratorów z tego samego kontrolera DPC

Administratorzy dodatkowego użytkownika muszą należeć do tego samego pakietu co administrator urządzenia w pełni zarządzanego. Aby uprościć programowanie, zalecamy udostępnianie administratora między urządzeniem a użytkownikami pomocniczymi.

Zarządzanie wieloma użytkownikami na określonych urządzeniach zwykle wymaga Androida 9.0, ale niektóre metody opisane w tym przewodniku dla programistów są dostępne we wcześniejszych wersjach tego systemu.

Tworzenie użytkowników

DPC może tworzyć dodatkowych użytkowników w tle i przełączać ich na pierwszy plan. Proces ten wygląda prawie tak samo w przypadku użytkowników pomocniczych i efemerycznych. Wykonaj te czynności na kontach administratorów w pełni zarządzanego urządzenia i użytkownika dodatkowego:

  1. Zadzwoń pod numer DevicePolicyManager.createAndManageUser(). Aby utworzyć użytkownika efemerycznego, w argumencie flag uwzględnij MAKE_USER_EPHEMERAL.
  2. Wywołaj DevicePolicyManager.startUserInBackground(), aby uruchomić użytkownika w tle. Użytkownik zaczyna działać, ale trzeba dokończyć konfigurację, zanim przeniesiesz go na pierwszy plan i pokażesz osobie korzystającej z urządzenia.
  3. Na koncie administratora użytkownika dodatkowego wywołaj DevicePolicyManager.setAffiliationIds(), aby powiązać nowego użytkownika z użytkownikiem głównym. Więcej informacji o koordynacji działań DPC znajdziesz poniżej.
  4. Wróć do administratora w pełni zarządzanego urządzenia i wywołaj metodę DevicePolicyManager.switchUser(), aby przełączyć użytkownika na pierwszy plan.

Ten przykład pokazuje, jak dodać krok 1 do DPC:

Kotlin

val dpm = getContext().getSystemService(Context.DEVICE_POLICY_SERVICE)
        as DevicePolicyManager

// If possible, reuse an existing affiliation ID across the
// primary user and (later) the ephemeral user.
val identifiers = dpm.getAffiliationIds(adminName)
if (identifiers.isEmpty()) {
    identifiers.add(UUID.randomUUID().toString())
    dpm.setAffiliationIds(adminName, identifiers)
}

// Pass an affiliation ID to the ephemeral user in the admin extras.
val adminExtras = PersistableBundle()
adminExtras.putString(AFFILIATION_ID_KEY, identifiers.first())
// Include any other config for the new user here ...

// Create the ephemeral user, using this component as the admin.
try {
    val ephemeralUser = dpm.createAndManageUser(
            adminName,
            "tmp_user",
            adminName,
            adminExtras,
            DevicePolicyManager.MAKE_USER_EPHEMERAL or
                    DevicePolicyManager.SKIP_SETUP_WIZARD)

} catch (e: UserManager.UserOperationException) {
    if (e.userOperationResult ==
            UserManager.USER_OPERATION_ERROR_MAX_USERS) {
        // Find a way to free up users...
    }
}

Java

DevicePolicyManager dpm = (DevicePolicyManager)
    getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);

// If possible, reuse an existing affiliation ID across the
// primary user and (later) the ephemeral user.
Set<String> identifiers = dpm.getAffiliationIds(adminName);
if (identifiers.isEmpty()) {
  identifiers.add(UUID.randomUUID().toString());
  dpm.setAffiliationIds(adminName, identifiers);
}

// Pass an affiliation ID to the ephemeral user in the admin extras.
PersistableBundle adminExtras = new PersistableBundle();
adminExtras.putString(AFFILIATION_ID_KEY, identifiers.iterator().next());
// Include any other config for the new user here ...

// Create the ephemeral user, using this component as the admin.
try {
  UserHandle ephemeralUser = dpm.createAndManageUser(
      adminName,
      "tmp_user",
      adminName,
      adminExtras,
      DevicePolicyManager.MAKE_USER_EPHEMERAL |
          DevicePolicyManager.SKIP_SETUP_WIZARD);

} catch (UserManager.UserOperationException e) {
  if (e.getUserOperationResult() ==
      UserManager.USER_OPERATION_ERROR_MAX_USERS) {
    // Find a way to free up users...
  }
}

Gdy tworzysz lub uruchamiasz nowego użytkownika, możesz sprawdzić przyczynę błędów, przesyłając wyjątek UserOperationException i wywołując getUserOperationResult(). Przekroczenie limitów użytkowników to częsta przyczyna niepowodzenia:

Tworzenie użytkownika może trochę potrwać. Jeśli często tworzysz konta użytkowników, możesz zadbać o ich komfort, przygotowując użytkowników gotowych do użycia w tle. Konieczne może być zachowanie równowagi między zaletami gotowego użytkownika a maksymalną liczbą użytkowników dozwolonych na urządzeniu.

Identyfikacja

Gdy utworzysz nowe konto użytkownika, skontaktuj się z nim za pomocą stałego numeru seryjnego. Nie utrzymuj UserHandle, ponieważ system używa ich ponownie podczas tworzenia i usuwania kont użytkowników. Aby uzyskać numer seryjny, zadzwoń pod numer UserManager.getSerialNumberForUser():

Kotlin

// After calling createAndManageUser() use a device-unique serial number
// (that isn’t recycled) to identify the new user.
secondaryUser?.let {
    val userManager = getContext().getSystemService(UserManager::class.java)
    val ephemeralUserId = userManager!!.getSerialNumberForUser(it)
    // Save the serial number to storage  ...
}

Java

// After calling createAndManageUser() use a device-unique serial number
// (that isn’t recycled) to identify the new user.
if (secondaryUser != null) {
  UserManager userManager = getContext().getSystemService(UserManager.class);
  long ephemeralUserId = userManager.getSerialNumberForUser(secondaryUser);
  // Save the serial number to storage  ...
}

Konfiguracja użytkownika

W zależności od potrzeb użytkowników możesz dostosować ich konfigurację. Podczas wywoływania funkcji createAndManageUser() możesz dodać te flagi:

SKIP_SETUP_WIZARD
Pomija uruchamianie kreatora konfiguracji nowego użytkownika, który sprawdza dostępność aktualizacji i instaluje je, prosi użytkownika o dodanie konta Google wraz z usługami Google i ustawia blokadę ekranu. Może to zająć trochę czasu i nie dotyczy wszystkich użytkowników – na przykład publicznych kiosków internetowych.
LEAVE_ALL_SYSTEM_APPS_ENABLED
Pozostawia wszystkie aplikacje systemowe włączone u nowego użytkownika. Jeśli nie ustawisz tej flagi, nowy użytkownik będzie korzystać z minimalnego zestawu aplikacji, które musi obsługiwać telefon – zwykle są to przeglądarka plików, telefon, kontakty i SMS-y.

Przestrzeganie cyklu życia użytkownika

DPC (jeśli jest administratorem w pełni zarządzanego urządzenia) może potrzebować informacji o zmianie użytkowników pomocniczych. Aby uruchamiać zadania wykonywane po wprowadzeniu zmian, zastąp te metody wywołania zwrotnego w podklasie DeviceAdminReceiver DPC:

onUserStarted()
Wywoływane po uruchomieniu konta użytkownika przez system. Ten użytkownik może jeszcze skonfigurować lub działać w tle. Użytkownika można uzyskać za pomocą argumentu startedUser.
onUserSwitched()
Wywoływane po przełączeniu systemu na innego użytkownika. Nowego użytkownika działającego na pierwszym planie można pobrać z argumentu switchedUser.
onUserStopped()
Wywoływane po zatrzymaniu użytkownika przez system, ponieważ wylogował się, przeszedł na nowego użytkownika (jeśli jest tymczasowy) lub DPC go zatrzymał. Użytkownika można użyć za pomocą argumentu stoppedUser.
onUserAdded()
Wywoływane, gdy system dodaje nowego użytkownika. Użytkownicy dodatkowi zwykle nie są w pełni konfigurowani, gdy DPC odbierze wywołanie zwrotne. Użytkownika można uzyskać za pomocą argumentu newUser.
onUserRemoved()
Wywoływane po usunięciu konta użytkownika przez system. Ponieważ użytkownik został już usunięty, nie masz dostępu do użytkownika reprezentowanego za pomocą argumentu removedUser.

Aby dowiedzieć się, kiedy system wyświetla użytkownika na pierwszym planie lub wysyła go w tle, aplikacje mogą zarejestrować odbiornik transmisji ACTION_USER_FOREGROUND i ACTION_USER_BACKGROUND.

Odkrywanie użytkowników

Aby pobrać wszystkich użytkowników pomocniczych, administrator w pełni zarządzanego urządzenia może wywołać metodę DevicePolicyManager.getSecondaryUsers(). Wyniki obejmują wszystkich użytkowników pomocniczych lub tymczasowych utworzonych przez administratora. Wyniki obejmują też wszystkich użytkowników dodatkowych (lub gości), które mogły utworzyć osoby korzystające z urządzenia. Wyniki nie obejmują profili służbowych, ponieważ nie są to użytkownicy dodatkowi. Poniższy przykład pokazuje, jak zastosować tę metodę:

Kotlin

// The device is stored for the night. Stop all running secondary users.
dpm.getSecondaryUsers(adminName).forEach {
    dpm.stopUser(adminName, it)
}

Java

// The device is stored for the night. Stop all running secondary users.
for (UserHandle user : dpm.getSecondaryUsers(adminName)) {
  dpm.stopUser(adminName, user);
}

Oto inne metody sprawdzania stanu użytkowników pomocniczych:

DevicePolicyManager.isEphemeralUser()
Wywołaj tę metodę przez administratora użytkownika dodatkowego, aby sprawdzić, czy jest to użytkownik tymczasowy.
DevicePolicyManager.isAffiliatedUser()
Wywołaj tę metodę u administratora użytkownika dodatkowego, aby sprawdzić, czy ten użytkownik jest powiązany z użytkownikiem głównym. Więcej informacji o powiązaniach znajdziesz w sekcji Koordynacja DPC poniżej.

Zarządzanie użytkownikami

Jeśli chcesz całkowicie zarządzać cyklem życia użytkownika, możesz wywoływać interfejsy API, które dają precyzyjną kontrolę nad tym, kiedy i w jaki sposób urządzenie wprowadza zmiany u użytkowników. Możesz na przykład usunąć użytkownika, gdy nie używano go przez jakiś czas, lub wysłać niewysłane zamówienia na serwer, zanim dana osoba dokończy pracę.

Wyloguj

Android 9.0 dodał do ekranu blokady przycisk wylogowania, aby użytkownik urządzenia mógł zakończyć sesję. Po kliknięciu przycisku system zatrzyma użytkownika dodatkowego, usunie użytkownika, jeśli jest on tymczasowy, a użytkownik główny wróci na pierwszy plan. Android ukrywa ten przycisk, gdy główny użytkownik znajduje się na pierwszym planie, ponieważ główny użytkownik nie może się wylogować.

Android domyślnie nie wyświetla przycisku zakończenia sesji, ale administrator (urządzenia w pełni zarządzanego urządzenia) może go włączyć, wywołując stronę DevicePolicyManager.setLogoutEnabled(). Aby potwierdzić bieżący stan przycisku, wywołaj DevicePolicyManager.isLogoutEnabled().

Administrator użytkownika dodatkowego może programowo wylogować użytkownika i powrócić do użytkownika głównego. Najpierw potwierdź, że użytkownik dodatkowy i główni są powiązane, a potem wywołaj DevicePolicyManager.logoutUser(). Jeśli wylogowany użytkownik jest użytkownikiem tymczasowym, system zatrzymuje się, a następnie usuwa użytkownika.

Przełącz użytkownika

Aby przełączyć się na innego użytkownika dodatkowego, administrator w pełni zarządzanego urządzenia może wywołać metodę DevicePolicyManager.switchUser(). Dla Twojej wygody umożliwiamy przełączenie użytkownika null na głównego użytkownika.

Zatrzymywanie użytkownika

Aby zatrzymać użytkownika dodatkowego, DPC, który jest właścicielem w pełni zarządzanego urządzenia, może wywołać metodę DevicePolicyManager.stopUser(). Jeśli zatrzymany użytkownik jest użytkownikiem tymczasowym, zostaje zatrzymany, a następnie usuwany.

Zalecamy zatrzymywanie ich, gdy tylko jest to możliwe. Pozwoli to uniknąć przekroczenia maksymalnej liczby uruchomionych użytkowników na urządzeniu.

Usuwanie użytkownika

Aby trwale usunąć użytkownika dodatkowego, DPC może wywołać jedną z tych metod DevicePolicyManager:

  • Administrator w pełni zarządzanego urządzenia może zadzwonić pod numer removeUser().
  • Administrator użytkownika dodatkowego może wywołać wipeData().

System usuwa użytkowników tymczasowych, gdy są zalogowani, zatrzymani lub przestają się wyświetlać.

Wyłącz domyślny interfejs użytkownika

Jeśli DPC udostępnia interfejs do zarządzania użytkownikami, możesz wyłączyć wbudowany w Androida interfejs z wieloma użytkownikami. Możesz to zrobić, wywołując metodę DevicePolicyManager.setLogoutEnabled() i dodając ograniczenie DISALLOW_USER_SWITCH, jak pokazano w tym przykładzie:

Kotlin

// Explicitly disallow logging out using Android UI (disabled by default).
dpm.setLogoutEnabled(adminName, false)

// Disallow switching users in Android's UI. This DPC can still
// call switchUser() to manage users.
dpm.addUserRestriction(adminName, UserManager.DISALLOW_USER_SWITCH)

Java

// Explicitly disallow logging out using Android UI (disabled by default).
dpm.setLogoutEnabled(adminName, false);

// Disallow switching users in Android's UI. This DPC can still
// call switchUser() to manage users.
dpm.addUserRestriction(adminName, UserManager.DISALLOW_USER_SWITCH);

Osoba korzystająca z urządzenia nie może dodawać użytkowników pomocniczych za pomocą wbudowanego interfejsu Androida, ponieważ administratorzy w pełni zarządzanych urządzeń automatycznie dodają ograniczenie użytkownika DISALLOW_ADD_USER.

Komunikaty dotyczące sesji

Gdy osoba używająca urządzenia przełączy się na nowego użytkownika, Android wyświetli panel z wyróżnieniem przełącznika. Android wyświetla te komunikaty:

  • Komunikat informujący o rozpoczęciu sesji użytkownika, który jest wyświetlany, gdy urządzenie przełącza się na użytkownika dodatkowego z poziomu użytkownika głównego.
  • Wiadomość sesji użytkownika jest wyświetlana, gdy urządzenie wraca do użytkownika głównego od użytkownika dodatkowego.

System nie wyświetla komunikatów podczas przełączania się między dwoma użytkownikami pomocniczymi.

Ponieważ wiadomości mogą nie być odpowiednie we wszystkich sytuacjach, możesz zmienić ich treść. Jeśli na przykład Twoje rozwiązanie używa tymczasowych sesji użytkowników, możesz uwzględnić to w komunikatach Zatrzymywanie sesji przeglądarki i usuwanie danych osobistych...

System wyświetla wiadomość tylko przez kilka sekund, a każda wiadomość powinna być krótką i czytelną frazą. Aby dostosować wiadomości, administrator może wywołać metody DevicePolicyManager setStartUserSessionMessage() i setEndUserSessionMessage(), jak pokazano w tym przykładzie:

Kotlin

// Short, easy-to-read messages shown at the start and end of a session.
// In your app, store these strings in a localizable resource.
internal val START_USER_SESSION_MESSAGE = "Starting guest session…"
internal val END_USER_SESSION_MESSAGE = "Stopping & clearing data…"

// ...
dpm.setStartUserSessionMessage(adminName, START_USER_SESSION_MESSAGE)
dpm.setEndUserSessionMessage(adminName, END_USER_SESSION_MESSAGE)

Java

// Short, easy-to-read messages shown at the start and end of a session.
// In your app, store these strings in a localizable resource.
private static final String START_USER_SESSION_MESSAGE = "Starting guest session…";
private static final String END_USER_SESSION_MESSAGE = "Stopping & clearing data…";

// ...
dpm.setStartUserSessionMessage(adminName, START_USER_SESSION_MESSAGE);
dpm.setEndUserSessionMessage(adminName, END_USER_SESSION_MESSAGE);

Przekaż null, aby usunąć komunikaty niestandardowe i wrócić do domyślnych wiadomości Androida. Aby sprawdzić treść bieżącej wiadomości, zadzwoń pod numer getStartUserSessionMessage() lub getEndUserSessionMessage().

DPC powinien ustawić wiadomości zlokalizowane w bieżącym języku użytkownika. Musisz też zaktualizować komunikaty po zmianie języka użytkownika:

Kotlin

override fun onReceive(context: Context?, intent: Intent?) {
    // Added the <action android:name="android.intent.action.LOCALE_CHANGED" />
    // intent filter for our DeviceAdminReceiver subclass in the app manifest file.
    if (intent?.action === ACTION_LOCALE_CHANGED) {

        // Android's resources return a string suitable for the new locale.
        getManager(context).setStartUserSessionMessage(
                getWho(context),
                context?.getString(R.string.start_user_session_message))

        getManager(context).setEndUserSessionMessage(
                getWho(context),
                context?.getString(R.string.end_user_session_message))
    }
    super.onReceive(context, intent)
}

Java

public void onReceive(Context context, Intent intent) {
  // Added the <action android:name="android.intent.action.LOCALE_CHANGED" />
  // intent filter for our DeviceAdminReceiver subclass in the app manifest file.
  if (intent.getAction().equals(ACTION_LOCALE_CHANGED)) {

    // Android's resources return a string suitable for the new locale.
    getManager(context).setStartUserSessionMessage(
        getWho(context),
        context.getString(R.string.start_user_session_message));

    getManager(context).setEndUserSessionMessage(
        getWho(context),
        context.getString(R.string.end_user_session_message));
  }
  super.onReceive(context, intent);
}

Koordynacja DPC

Aby zarządzać użytkownikami dodatkowymi, zwykle potrzebne są 2 instancje DPC – jedna jest właścicielem w pełni zarządzanego urządzenia, a druga użytkownika dodatkowego. Podczas tworzenia nowego użytkownika administrator w pełni zarządzanego urządzenia ustawia inną instancję jako administratora nowego użytkownika.

Powiązani użytkownicy

Niektóre interfejsy API w tym przewodniku dla programistów działają tylko wtedy, gdy użytkownicy pomocniczy są powiązani. Ponieważ Android wyłącza niektóre funkcje (np. logowanie sieciowe), gdy dodajesz do urządzenia nowych niepowiązanych użytkowników drugorzędnych, warto jak najszybciej powiązać użytkowników z kontem. Przykład znajdziesz w sekcji Konfiguracja poniżej.

Skonfiguruj

Skonfiguruj nowych użytkowników pomocniczych (z jednostki DPC, do której należy użytkownik dodatkowy), zanim udostępnisz ich użytkownikom. Możesz to zrobić przy użyciu wywołania zwrotnego DeviceAdminReceiver.onEnabled(). Jeśli wcześniej ustawiono dodatki administratora w wywołaniu funkcji createAndManageUser(), możesz pobrać wartości z argumentu intent. Poniższy przykład pokazuje połączenie DPC z nowym użytkownikiem dodatkowym w wywołaniu zwrotnym:

Kotlin

override fun onEnabled(context: Context?, intent: Intent?) {
    super.onEnabled(context, intent)

    // Get the affiliation ID (our DPC previously put in the extras) and
    // set the ID for this new secondary user.
    intent?.getStringExtra(AFFILIATION_ID_KEY)?.let {
        val dpm = getManager(context)
        dpm.setAffiliationIds(getWho(context), setOf(it))
    }
    // Continue setup of the new secondary user ...
}

Java

public void onEnabled(Context context, Intent intent) {
  // Get the affiliation ID (our DPC previously put in the extras) and
  // set the ID for this new secondary user.
  String affiliationId = intent.getStringExtra(AFFILIATION_ID_KEY);
  if (affiliationId != null) {
    DevicePolicyManager dpm = getManager(context);
    dpm.setAffiliationIds(getWho(context),
        new HashSet<String>(Arrays.asList(affiliationId)));
  }
  // Continue setup of the new secondary user ...
}

Wywołania RPC między DPC

Mimo że te 2 instancje DPC działają w ramach osobnych użytkowników, DPC, do którego należy urządzenie, i użytkownicy pomocniczy mogą się ze sobą komunikować. Wywoływanie innej usługi DPC przekracza granice użytkowników, dlatego DPC nie może wywołać bindService() tak jak normalnie na Androidzie. Aby utworzyć powiązanie z usługą działającą w innym użytkowniku, wywołaj DevicePolicyManager.bindDeviceAdminServiceAsUser().

Użytkownik główny i 2 powiązanych użytkowników dodatkowych wywołujących RPC.
Rysunek 2. Administratorzy powiązanych użytkowników głównych i dodatkowych wywołujących metody usługi

DPC może powiązać tylko usługi działające w użytkownikach zwracanych przez DevicePolicyManager.getBindDeviceAdminTargetUsers(). Ten przykład przedstawia administratora dodatkowego powiązanego użytkownika z administratorem w pełni zarządzanego urządzenia:

Kotlin

// From a secondary user, the list contains just the primary user.
dpm.getBindDeviceAdminTargetUsers(adminName).forEach {

    // Set up the callbacks for the service connection.
    val intent = Intent(mContext, FullyManagedDeviceService::class.java)
    val serviceconnection = object : ServiceConnection {
        override fun onServiceConnected(componentName: ComponentName,
                                        iBinder: IBinder) {
            // Call methods on service ...
        }
        override fun onServiceDisconnected(componentName: ComponentName) {
            // Clean up or reconnect if needed ...
        }
    }

    // Bind to the service as the primary user [it].
    val bindSuccessful = dpm.bindDeviceAdminServiceAsUser(adminName,
            intent,
            serviceconnection,
            Context.BIND_AUTO_CREATE,
            it)
}

Java

// From a secondary user, the list contains just the primary user.
List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(adminName);
if (targetUsers.isEmpty()) {
  // If the users aren't affiliated, the list doesn't contain any users.
  return;
}

// Set up the callbacks for the service connection.
Intent intent = new Intent(mContext, FullyManagedDeviceService.class);
ServiceConnection serviceconnection = new ServiceConnection() {
  @Override
  public void onServiceConnected(
      ComponentName componentName, IBinder iBinder) {
    // Call methods on service ...
  }

  @Override
  public void onServiceDisconnected(ComponentName componentName) {
    // Clean up or reconnect if needed ...
  }
};

// Bind to the service as the primary user.
UserHandle primaryUser = targetUsers.get(0);
boolean bindSuccessful = dpm.bindDeviceAdminServiceAsUser(
    adminName,
    intent,
    serviceconnection,
    Context.BIND_AUTO_CREATE,
    primaryUser);

Dodatkowe materiały

Aby dowiedzieć się więcej o dedykowanych urządzeniach, przeczytaj te dokumenty: