Mengelola beberapa pengguna

Panduan developer ini menjelaskan bagaimana pengontrol kebijakan perangkat (DPC) dapat mengelola beberapa pengguna Android di perangkat khusus.

Ringkasan

DPC Anda dapat membantu beberapa orang berbagi satu perangkat khusus. DPC Anda yang berjalan di perangkat terkelola sepenuhnya dapat membuat dan mengelola dua jenis pengguna:

  • Pengguna sekunder adalah pengguna Android dengan aplikasi dan data terpisah yang disimpan di antara sesi. Anda mengelola pengguna dengan komponen admin. Pengguna ini berguna untuk kasus ketika perangkat diambil pada awal shift, seperti driver pengiriman atau pekerja keamanan.
  • Pengguna sementara adalah pengguna sekunder yang dihapus oleh sistem saat pengguna berhenti, beralih, atau perangkat dimulai ulang. Pengguna ini berguna untuk kasus ketika data dapat dihapus setelah sesi selesai, seperti kios internet akses publik.

Anda menggunakan DPC yang sudah ada untuk mengelola perangkat khusus dan pengguna sekunder. Komponen admin di DPC menetapkan dirinya sebagai admin bagi pengguna sekunder baru saat Anda membuatnya.

Pengguna utama dan dua pengguna sekunder.
Gambar 1. Pengguna utama dan sekunder yang dikelola oleh admin dari DPC yang sama

Admin pengguna sekunder harus termasuk dalam paket yang sama dengan admin perangkat terkelola sepenuhnya. Untuk menyederhanakan pengembangan, sebaiknya bagikan admin antara perangkat dan pengguna sekunder.

Mengelola banyak pengguna di perangkat khusus biasanya memerlukan Android 9.0, tetapi beberapa metode yang digunakan dalam panduan developer ini tersedia di versi Android yang lebih lama.

Membuat pengguna

DPC dapat membuat pengguna tambahan di latar belakang lalu mengalihkannya ke latar depan. Prosesnya hampir sama untuk pengguna sekunder dan singkat. Terapkan langkah-langkah berikut dalam admin perangkat terkelola sepenuhnya dan pengguna sekunder:

  1. Panggil DevicePolicyManager.createAndManageUser(). Untuk membuat pengguna singkat, sertakan MAKE_USER_EPHEMERAL dalam argumen flag.
  2. Panggil DevicePolicyManager.startUserInBackground() untuk memulai pengguna di latar belakang. Pengguna mulai berlari, tetapi Anda sebaiknya menyelesaikan penyiapan sebelum membawa pengguna ke latar depan dan menampilkannya kepada orang yang menggunakan perangkat.
  3. Dalam admin pengguna sekunder, panggil DevicePolicyManager.setAffiliationIds() untuk mengafiliasikan pengguna baru dengan pengguna utama. Lihat Koordinasi DPC di bawah.
  4. Kembali ke admin perangkat terkelola sepenuhnya, panggil DevicePolicyManager.switchUser() untuk mengalihkan pengguna ke latar depan.

Contoh berikut menunjukkan cara menambahkan langkah 1 ke 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...
  }
}

Saat membuat atau memulai pengguna baru, Anda dapat memeriksa alasan kegagalan dengan menangkap pengecualian UserOperationException dan memanggil getUserOperationResult(). Melebihi batas pengguna adalah alasan kegagalan umum:

Membuat pengguna dapat memerlukan beberapa waktu. Jika sering membuat pengguna, Anda dapat meningkatkan pengalaman pengguna dengan menyiapkan pengguna siap pakai di latar belakang. Anda mungkin perlu menyeimbangkan keuntungan pengguna yang siap pakai dengan jumlah maksimum pengguna yang diizinkan di perangkat.

Identifikasi

Setelah membuat pengguna baru, Anda harus merujuk ke pengguna dengan nomor seri persisten. Jangan pertahankan UserHandle karena sistem akan mendaur ulangnya saat Anda membuat dan menghapus pengguna. Dapatkan nomor seri dengan memanggil 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  ...
}

Konfigurasi pengguna

Bergantung pada kebutuhan pengguna, Anda dapat menyesuaikan penyiapan pengguna sekunder. Anda dapat menyertakan tanda berikut saat memanggil createAndManageUser():

SKIP_SETUP_WIZARD
Lewati menjalankan wizard penyiapan pengguna baru yang memeriksa dan menginstal update, meminta pengguna untuk menambahkan Akun Google bersama dengan layanan Google, dan menetapkan kunci layar. Proses ini mungkin memerlukan waktu beberapa saat dan mungkin tidak berlaku untuk semua pengguna—misalnya, kios internet publik.
LEAVE_ALL_SYSTEM_APPS_ENABLED
Membiarkan semua aplikasi sistem tetap aktif di pengguna baru. Jika tanda ini tidak disetel, pengguna baru hanya akan berisi kumpulan aplikasi minimal yang diperlukan ponsel untuk beroperasi—biasanya file browser, aplikasi telepon, kontak, dan pesan SMS.

Mengikuti siklus proses pengguna

DPC Anda (jika merupakan admin perangkat terkelola sepenuhnya) mungkin akan berguna untuk mengetahui kapan pengguna sekunder berubah. Untuk menjalankan tugas lanjutan setelah perubahan, ganti metode callback ini di subclass DeviceAdminReceiver DPC:

onUserStarted()
Dipanggil setelah sistem memulai pengguna. Pengguna ini mungkin masih menyiapkan atau berjalan di latar belakang. Anda bisa mendapatkan pengguna dari argumen startedUser.
onUserSwitched()
Dipanggil setelah sistem beralih ke pengguna lain. Anda bisa mendapatkan pengguna baru yang sekarang berjalan di latar depan dari argumen switchedUser.
onUserStopped()
Dipanggil setelah sistem menghentikan pengguna karena mereka telah logout, beralih ke pengguna baru (jika pengguna bersifat sementara), atau DPC Anda menghentikan pengguna. Anda bisa mendapatkan pengguna dari argumen stoppedUser.
onUserAdded()
Dipanggil saat sistem menambahkan pengguna baru. Biasanya, pengguna sekunder tidak sepenuhnya disiapkan saat DPC mendapatkan callback. Anda bisa mendapatkan pengguna dari argumen newUser.
onUserRemoved()
Dipanggil setelah sistem menghapus pengguna. Karena pengguna sudah dihapus, Anda tidak dapat mengakses pengguna yang diwakili oleh argumen removedUser.

Untuk mengetahui kapan sistem membawa pengguna ke latar depan atau mengarahkan pengguna ke latar belakang, aplikasi dapat mendaftarkan penerima untuk siaran ACTION_USER_FOREGROUND dan ACTION_USER_BACKGROUND.

Menemukan pengguna

Untuk mendapatkan semua pengguna sekunder, admin perangkat yang terkelola sepenuhnya dapat memanggil DevicePolicyManager.getSecondaryUsers(). Hasilnya mencakup pengguna sekunder atau sementara yang dibuat admin. Hasilnya juga menyertakan pengguna sekunder (atau pengguna tamu) yang mungkin telah dibuat oleh pengguna perangkat. Hasilnya tidak menyertakan profil kerja karena bukan pengguna sekunder. Contoh berikut menunjukkan cara menggunakan metode ini:

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);
}

Berikut adalah metode lain yang dapat Anda panggil untuk mengetahui status pengguna sekunder:

DevicePolicyManager.isEphemeralUser()
Panggil metode ini dari admin pengguna sekunder untuk mengetahui apakah ini adalah pengguna sementara.
DevicePolicyManager.isAffiliatedUser()
Panggil metode ini dari admin pengguna sekunder untuk mengetahui apakah pengguna ini berafiliasi dengan pengguna utama. Untuk mempelajari afiliasi lebih lanjut, lihat koordinasi DPC di bawah.

Pengelolaan pengguna

Jika ingin sepenuhnya mengelola siklus proses pengguna, Anda dapat memanggil API untuk kontrol yang mendetail mengenai waktu dan cara perangkat mengubah pengguna. Misalnya, Anda dapat menghapus pengguna jika perangkat tidak digunakan selama jangka waktu tertentu atau Anda dapat mengirim pesanan yang tidak terkirim ke server sebelum shift seseorang selesai.

Logout

Android 9.0 menambahkan tombol logout ke layar kunci sehingga pengguna perangkat dapat mengakhiri sesinya. Setelah mengetuk tombol tersebut, sistem akan menghentikan pengguna sekunder, menghapus pengguna jika bersifat singkat, dan pengguna utama kembali ke latar depan. Android menyembunyikan tombol saat pengguna utama ada di latar depan karena pengguna utama tidak dapat logout.

Android tidak menampilkan tombol akhiri sesi secara default, tetapi admin Anda (dari perangkat terkelola sepenuhnya) dapat mengaktifkannya dengan memanggil DevicePolicyManager.setLogoutEnabled(). Jika Anda perlu mengonfirmasi status tombol saat ini, panggil DevicePolicyManager.isLogoutEnabled().

Admin pengguna sekunder dapat membuat pengguna logout secara terprogram dan kembali ke pengguna utama. Pertama, pastikan pengguna sekunder dan pengguna utama berafiliasi, lalu panggil DevicePolicyManager.logoutUser(). Jika pengguna yang logout adalah pengguna sementara, sistem akan menghentikan lalu menghapus pengguna tersebut.

Ganti pengguna

Untuk beralih ke pengguna sekunder lain, admin perangkat terkelola sepenuhnya dapat memanggil DevicePolicyManager.switchUser(). Untuk memudahkan, Anda dapat meneruskan null untuk beralih ke pengguna utama.

Menghentikan pengguna

Untuk menghentikan pengguna sekunder, DPC yang memiliki perangkat terkelola sepenuhnya dapat memanggil DevicePolicyManager.stopUser(). Jika pengguna yang dihentikan adalah pengguna singkat, pengguna tersebut akan dihentikan lalu dihapus.

Jika memungkinkan, sebaiknya hentikan pengguna agar tidak melebihi jumlah maksimum pengguna yang menjalankan perangkat.

Menghapus pengguna

Untuk menghapus pengguna sekunder secara permanen, DPC dapat memanggil salah satu metode DevicePolicyManager berikut:

  • Admin perangkat terkelola sepenuhnya dapat memanggil removeUser().
  • Admin pengguna sekunder dapat memanggil wipeData().

Sistem akan menghapus pengguna singkat saat mereka logout, dihentikan, atau beralih.

Menonaktifkan UI default

Jika DPC menyediakan UI untuk mengelola pengguna, Anda dapat menonaktifkan antarmuka multi-pengguna bawaan Android. Anda dapat melakukannya dengan memanggil DevicePolicyManager.setLogoutEnabled() dan menambahkan pembatasan DISALLOW_USER_SWITCH seperti ditunjukkan dalam contoh berikut:

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);

Pengguna yang menggunakan perangkat tidak dapat menambahkan pengguna sekunder dengan UI bawaan Android karena admin perangkat terkelola sepenuhnya otomatis menambahkan batasan pengguna DISALLOW_ADD_USER.

Pesan sesi

Saat orang yang menggunakan perangkat beralih ke pengguna baru, Android akan menampilkan panel untuk menandai tombol tersebut. Android akan menampilkan pesan berikut:

  • Pesan sesi pengguna awal yang ditampilkan saat perangkat beralih ke pengguna sekunder dari pengguna utama.
  • Pesan sesi pengguna akhir yang ditampilkan saat perangkat kembali ke pengguna utama dari pengguna sekunder.

Sistem tidak menampilkan pesan saat beralih di antara dua pengguna sekunder.

Karena pesan mungkin tidak cocok untuk semua situasi, Anda dapat mengubah teks pesan tersebut. Misalnya, jika solusi Anda menggunakan sesi pengguna singkat, Anda dapat merefleksikannya dalam pesan seperti: Menghentikan sesi browser & menghapus data pribadi...

Sistem menampilkan pesan hanya selama beberapa detik, jadi setiap pesan harus berupa frasa yang singkat dan jelas. Untuk menyesuaikan pesan, admin Anda dapat memanggil metode DevicePolicyManager setStartUserSessionMessage() dan setEndUserSessionMessage() seperti yang ditunjukkan dalam contoh berikut:

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);

Teruskan null untuk menghapus pesan kustom Anda dan kembali ke pesan default Android. Jika Anda perlu memeriksa teks pesan saat ini, panggil getStartUserSessionMessage() atau getEndUserSessionMessage().

DPC harus menetapkan pesan yang dilokalkan untuk lokalitas pengguna saat ini. Anda juga perlu mengupdate pesan saat lokalitas pengguna berubah:

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);
}

Koordinasi DPC

Mengelola pengguna sekunder biasanya memerlukan dua instance DPC—salah satu yang memiliki perangkat terkelola sepenuhnya sedangkan yang lain memiliki pengguna sekunder. Saat membuat pengguna baru, admin perangkat terkelola sepenuhnya menetapkan instance lain dirinya sebagai admin pengguna baru.

Pengguna terafiliasi

Beberapa API dalam panduan developer ini hanya berfungsi jika pengguna sekunder berafiliasi. Karena Android menonaktifkan beberapa fitur (misalnya, logging jaringan) saat Anda menambahkan pengguna sekunder baru yang tidak terafiliasi ke perangkat, Anda harus berafiliasi dengan pengguna sesegera mungkin. Lihat contoh dalam Penyiapan di bawah.

Penyiapan

Siapkan pengguna sekunder baru (dari DPC yang memiliki pengguna sekunder) sebelum mengizinkan orang menggunakannya. Anda dapat melakukan penyiapan ini dari callback DeviceAdminReceiver.onEnabled(). Jika sebelumnya Anda telah menetapkan tambahan admin dalam panggilan ke createAndManageUser(), Anda bisa mendapatkan nilai dari argumen intent. Contoh berikut menunjukkan DPC yang mengafiliasi pengguna sekunder baru di callback:

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 ...
}

RPC antar-DPC

Meskipun kedua instance DPC berjalan di bawah pengguna terpisah, DPC yang memiliki perangkat dan pengguna sekunder dapat saling berkomunikasi. Karena memanggil layanan DPC lain melewati batas pengguna, DPC tidak dapat memanggil bindService() seperti yang biasanya Anda lakukan di Android. Untuk mengikat ke layanan yang berjalan di pengguna lain, panggil DevicePolicyManager.bindDeviceAdminServiceAsUser().

Pengguna utama dan dua pengguna sekunder terafiliasi yang memanggil RPC.
Gambar 2. Admin pengguna utama dan sekunder yang berafiliasi memanggil metode layanan

DPC Anda hanya dapat mengikat ke layanan yang berjalan di pengguna yang ditampilkan oleh DevicePolicyManager.getBindDeviceAdminTargetUsers(). Contoh berikut menunjukkan admin binding pengguna sekunder ke admin perangkat terkelola sepenuhnya:

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);

Referensi lainnya

Untuk mempelajari perangkat khusus lebih lanjut, baca dokumen berikut: