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.
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:
- Panggil
DevicePolicyManager.createAndManageUser()
. Untuk membuat pengguna singkat, sertakanMAKE_USER_EPHEMERAL
dalam argumen flag. - 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. - Di admin pengguna sekunder, panggil
DevicePolicyManager.setAffiliationIds()
untuk mengafiliasi pengguna baru dengan pengguna utama. Lihat Koordinasi DPC di bawah. - 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()
.
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:
- Ringkasan perangkat khusus adalah ringkasan perangkat khusus.
- Mode kunci tugas menjelaskan cara mengunci perangkat khusus ke satu atau kumpulan aplikasi.
- Buku resep perangkat khusus dengan contoh lebih lanjut untuk membatasi perangkat khusus dan meningkatkan pengalaman pengguna.