Mengelola beberapa pengguna

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

Ringkasan

DPC Anda dapat membantu banyak 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 ephemeral 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 ada untuk mengelola perangkat khusus dan pengguna sekunder. Komponen admin di DPC menetapkan dirinya sebagai admin untuk 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.

Pengguna sekunder

Pengguna sekunder dapat terhubung ke Wi-Fi dan dapat mengonfigurasi jaringan baru. Namun, mereka tidak dapat mengedit atau menghapus jaringan, bahkan jaringan yang mereka buat.

Membuat pengguna

DPC dapat membuat pengguna tambahan di latar belakang, lalu dapat mengalihkannya ke latar depan. Prosesnya hampir sama untuk pengguna sekunder dan efemeral. Terapkan langkah-langkah berikut pada 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. Di admin pengguna sekunder, panggil DevicePolicyManager.setAffiliationIds() untuk mengafiliasi 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 yang siap pakai di latar belakang. Anda mungkin perlu menyeimbangkan keuntungan pengguna siap pakai dengan jumlah maksimum pengguna yang diizinkan di perangkat.

Identifikasi

Setelah membuat pengguna baru, Anda harus merujuk pengguna tersebut dengan nomor seri tetap. 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
Tidak usah menjalankan wizard penyiapan pengguna baru yang memeriksa dan menginstal update, meminta pengguna untuk menambahkan Akun Google bersama dengan layanan Google, dan menyetel 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
Mengizinkan semua aplikasi sistem yang diaktifkan di pengguna baru. Jika Anda tidak menyetel tanda ini, pengguna baru hanya akan berisi kumpulan aplikasi minimal yang diperlukan ponsel—biasanya browser file, telepon telepon, kontak, dan pesan SMS.

Mengikuti siklus proses pengguna

DPC (jika ini admin dari perangkat terkelola sepenuhnya) mungkin perlu 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 pengguna telah logout, beralih ke pengguna baru (jika pengguna bersifat sementara), atau DPC Anda menghentikan pengguna. Anda dapat memperoleh 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.

Temukan pengguna

Untuk mendapatkan semua pengguna sekunder, admin perangkat terkelola sepenuhnya dapat memanggil DevicePolicyManager.getSecondaryUsers(). Hasilnya mencakup semua pengguna sekunder atau sementara yang dibuat admin. Hasilnya juga menyertakan semua pengguna sekunder (atau pengguna tamu) yang mungkin telah dibuat oleh pengguna yang menggunakan 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 atau tidak. Untuk mempelajari afiliasi lebih lanjut, lihat koordinasi DPC di bawah.

Pengelolaan pengguna

Jika ingin mengelola siklus proses pengguna sepenuhnya, Anda dapat memanggil API untuk mengontrol waktu dan cara perangkat mengubah pengguna secara mendetail. 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 yang menggunakan perangkat tersebut dapat mengakhiri sesi mereka. Setelah tombol tersebut diketuk, sistem akan menghentikan pengguna sekunder, menghapus pengguna jika bersifat sementara, dan pengguna utama kembali ke latar depan. Android menyembunyikan tombol saat pengguna utama berada di latar depan karena pengguna utama tidak dapat logout.

Android tidak menampilkan tombol akhir 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, konfirmasi pengguna sekunder dan utama terafiliasi, lalu panggil DevicePolicyManager.logoutUser(). Jika pengguna yang logout adalah pengguna sementara, sistem akan berhenti, lalu menghapus pengguna tersebut.

Ganti pengguna

Untuk beralih ke pengguna sekunder lain, admin perangkat yang 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 sementara, pengguna tersebut akan dihentikan, lalu dihapus.

Sebaiknya hentikan pengguna jika memungkinkan agar jumlah pengguna yang berjalan tidak melebihi batas maksimum perangkat yang berjalan.

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 batasan 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 perangkat tidak dapat menambahkan pengguna sekunder dengan UI bawaan Android karena admin perangkat terkelola sepenuhnya menambahkan batasan pengguna DISALLOW_ADD_USER secara otomatis.

Pesan sesi

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

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

Sistem tidak menampilkan pesan saat beralih 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 efemeral, Anda dapat mencerminkan hal ini 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 Anda harus menyetel pesan yang dilokalkan untuk lokalitas pengguna saat ini. Anda juga perlu memperbarui 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 dari dirinya sendiri sebagai admin pengguna baru.

Pengguna terafiliasi

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

Penyiapan

Siapkan pengguna sekunder baru (dari DPC yang memiliki pengguna sekunder) sebelum mengizinkan pengguna menggunakannya. Anda dapat melakukan penyiapan ini dari callback DeviceAdminReceiver.onEnabled(). Jika sebelumnya Anda menetapkan tambahan admin dalam panggilan ke createAndManageUser(), Anda bisa mendapatkan nilai dari argumen intent. Contoh berikut menunjukkan DPC yang mengafiliasi pengguna sekunder baru dalam 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 berkomunikasi satu sama lain. Karena memanggil layanan DPC lain melewati batas pengguna, DPC Anda tidak dapat memanggil bindService() seperti yang biasanya dilakukan 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 terafiliasi yang 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 tambahan

Untuk mempelajari perangkat khusus lebih lanjut, baca dokumen berikut: