本教戰手冊可協助開發人員和系統整合商強化專屬裝置解決方案。按照操作方法尋找專用裝置行為的解決方案。本教戰手冊最適合已擁有裝置專用應用程式的開發人員。如果您才剛開始使用,請參閱「專用裝置總覽」一文。
自訂 Google Home 應用程式
如果您開發的應用程式會取代 Android 主畫面和啟動器,這些方案都非常實用。
當 Google Home 應用程式
您可以將應用程式設為裝置的主畫面應用程式,使其在裝置啟動時自動啟動。您也可以啟用「主畫面」按鈕,在鎖定任務模式下將許可清單中的應用程式移至前景。
所有主畫面應用程式都會處理 CATEGORY_HOME
意圖類別,這就是系統辨識主畫面應用程式的方式。如要成為預設的主畫面應用程式,請呼叫 DevicePolicyManager.addPersistentPreferredActivity()
,將應用程式的其中一個活動設為偏好的主畫面意圖處理常式,如以下範例所示:
Kotlin
// Create an intent filter to specify the Home category. val filter = IntentFilter(Intent.ACTION_MAIN) filter.addCategory(Intent.CATEGORY_HOME) filter.addCategory(Intent.CATEGORY_DEFAULT) // Set the activity as the preferred option for the device. val activity = ComponentName(context, KioskModeActivity::class.java) val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager dpm.addPersistentPreferredActivity(adminName, filter, activity)
Java
// Create an intent filter to specify the Home category. IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); // Set the activity as the preferred option for the device. ComponentName activity = new ComponentName(context, KioskModeActivity.class); DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); dpm.addPersistentPreferredActivity(adminName, filter, activity);
您仍須在應用程式資訊清單檔案中宣告意圖篩選器,如以下 XML 程式碼片段所示:
<activity
android:name=".KioskModeActivity"
android:label="@string/kiosk_mode"
android:launchMode="singleInstance"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
一般來說,不會希望啟動器應用程式顯示在「總覽」畫面中。不過,您不需要將 excludeFromRecents
加入活動宣告中,因為系統在鎖定任務模式下執行時,Android 啟動器會隱藏最初啟動的活動。
顯示個別工作
FLAG_ACTIVITY_NEW_TASK
對啟動器類型應用程式而言相當實用,因為每個新工作都會在「總覽」畫面中顯示為個別項目。如要進一步瞭解「總覽」畫面中的工作,請參閱「最近使用」畫面。
公用資訊站
這些食譜很適合在公共空間中的無人負責裝置,但也有助於許多專用裝置使用者專注在工作。
鎖定裝置
為確保裝置適用於其用途,您可以新增表 1 中列出的使用者限制。
使用者限制 | 說明 |
---|---|
DISALLOW_FACTORY_RESET |
禁止裝置使用者將裝置恢復原廠設定。全代管裝置的管理員和主要使用者可以設定這項限制。 |
DISALLOW_SAFE_BOOT |
禁止裝置使用者以安全模式啟動裝置,系統就不會自動啟動您的應用程式。全代管裝置管理員和主要使用者可以設定這項限制。 |
DISALLOW_MOUNT_PHYSICAL_MEDIA |
防止裝置使用者掛接可能連接至裝置的任何儲存磁碟區。全代管裝置的管理員和主要使用者可以設定這項限制。 |
DISALLOW_ADJUST_VOLUME |
將裝置設為靜音,並防止裝置使用者變更音量和震動設定。確認資訊站不需要音訊來播放媒體或無障礙功能。全代管裝置、主要使用者、次要使用者和工作資料夾的管理員都可以設定這項限制。 |
DISALLOW_ADD_USER |
防止裝置使用者新增新使用者,例如次要使用者或受限制的使用者。系統會自動對全代管裝置新增這項使用者限制,但這項設定可能已遭清除。全代管裝置的管理員和主要使用者可以設定這項限制。 |
下列程式碼片段說明如何設定限制:
Kotlin
// If the system is running in lock task mode, set the user restrictions // for a kiosk after launching the activity. arrayOf( UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_SAFE_BOOT, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_ADD_USER).forEach { dpm.addUserRestriction(adminName, it) }
Java
// If the system is running in lock task mode, set the user restrictions // for a kiosk after launching the activity. String[] restrictions = { UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_SAFE_BOOT, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_ADD_USER}; for (String restriction: restrictions) dpm.addUserRestriction(adminName, restriction);
建議您在應用程式處於管理員模式時移除這些限制,確保 IT 管理員仍可使用這些功能進行裝置維護。如要清除限制,請呼叫 DevicePolicyManager.clearUserRestriction()
。
隱藏錯誤對話方塊
在某些環境中,例如零售商示範或公開資訊顯示,您可能不想向使用者顯示錯誤對話方塊。在 Android 9.0 (API 級別 28) 以上版本中,您可以新增 DISALLOW_SYSTEM_ERROR_DIALOGS
使用者限制,藉此隱藏應用程式當機或無回應時的系統錯誤對話方塊。系統會像裝置使用者透過對話方塊關閉應用程式一樣,重新啟動無回應的應用程式。請參考以下範例:
Kotlin
override fun onEnabled(context: Context, intent: Intent) { val dpm = getManager(context) val adminName = getWho(context) dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS) }
Java
public void onEnabled(Context context, Intent intent) { DevicePolicyManager dpm = getManager(context); ComponentName adminName = getWho(context); dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS); }
如果主要或次要使用者的管理員設定了這項限制,系統會僅隱藏該使用者的錯誤對話方塊。如果全代管裝置的管理員設定了這項限制,系統會隱藏所有使用者的對話方塊。
讓螢幕保持開啟
建構資訊站時,您可以在執行應用程式活動時停止讓裝置進入休眠狀態。在應用程式視窗中加入 FLAG_KEEP_SCREEN_ON
版面配置旗標,如以下範例所示:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Keep the screen on and bright while this kiosk activity is running. window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Keep the screen on and bright while this kiosk activity is running. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); }
建議您檢查裝置是否已插入 AC、USB 或無線充電器。註冊電池變更廣播,並使用 BatteryManager
值探索充電狀態。如果裝置未接上電源,您還可以傳送遠端快訊給 IT 管理員。如需逐步操作說明,請參閱「監控電池電量和充電狀態」。
您也可以設定 STAY_ON_WHILE_PLUGGED_IN
全域設定,讓裝置在接上電源時保持喚醒狀態。在 Android 6.0 (API 級別 23) 以上版本中,全代管裝置的管理員可以呼叫 DevicePolicyManager.setGlobalSetting()
,如以下範例所示:
Kotlin
val pluggedInto = BatteryManager.BATTERY_PLUGGED_AC or BatteryManager.BATTERY_PLUGGED_USB or BatteryManager.BATTERY_PLUGGED_WIRELESS dpm.setGlobalSetting(adminName, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, pluggedInto.toString())
Java
int pluggedInto = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS; dpm.setGlobalSetting( adminName, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, String.valueOf(pluggedInto));
應用程式套件
本節說明如何在專用裝置上有效率地安裝應用程式。
快取應用程式套件
如果共用裝置的使用者全都共用同一組應用程式,請盡可能避免下載應用程式。如要在與一組固定使用者 (例如位移工作站的裝置) 上簡化共用裝置上的使用者帳戶,您可以在 Android 9.0 (API 級別 28) 以上版本中快取多使用者工作階段所需的應用程式套件 (APK)。
安裝在裝置上的快取 APK 會分為以下兩個階段:
- 全代管裝置 (或委派代表,請參閱下文) 的管理元件會設定要存放在裝置中的 APK 清單。
- 關聯的次要使用者 (或其委派對象) 的管理元件可以代表使用者安裝快取 APK。全代管裝置、主要使用者或關聯工作資料夾 (或其委派對象) 的管理員還可以視需要安裝快取的應用程式。
如要設定要保留在裝置上的 APK 清單,管理員會呼叫 DevicePolicyManager.setKeepUninstalledPackages()
。
這個方法不會檢查 APK 是否已安裝在裝置上。如果您想在使用者需要應用程式前就先安裝應用程式,這種做法非常實用。如要取得先前設定的套件清單,可以呼叫 DevicePolicyManager.getKeepUninstalledPackages()
。在您透過變更呼叫 setKeepUninstalledPackages()
或刪除次要使用者時,系統會刪除不再需要的快取 APK。
如要安裝快取 APK,請呼叫 DevicePolicyManager.installExistingPackage()
。這個方法只能安裝系統已快取的應用程式;專用的裝置解決方案 (或裝置使用者) 必須先在裝置上安裝應用程式,才能呼叫此方法。
以下範例說明如何在全代管裝置和次要使用者的管理員中使用這些 API 呼叫:
Kotlin
// Set the package to keep. This method assumes that the package is already // installed on the device by managed Google Play. val cachedAppPackageName = "com.example.android.myapp" dpm.setKeepUninstalledPackages(adminName, listOf(cachedAppPackageName)) // ... // The admin of a secondary user installs the app. val success = dpm.installExistingPackage(adminName, cachedAppPackageName)
Java
// Set the package to keep. This method assumes that the package is already // installed on the device by managed Google Play. String cachedAppPackageName = "com.example.android.myapp"; List<String> packages = new ArrayList<String>(); packages.add(cachedAppPackageName); dpm.setKeepUninstalledPackages(adminName, packages); // ... // The admin of a secondary user installs the app. boolean success = dpm.installExistingPackage(adminName, cachedAppPackageName);
委派應用程式
你可以委派其他應用程式來管理應用程式快取。這麼做可以區隔解決方案的功能,或讓 IT 管理員使用自己的應用程式。委派應用程式會取得與管理元件相同的權限。例如,次要使用者管理員的應用程式委派可以呼叫 installExistingPackage()
,但無法呼叫 setKeepUninstalledPackages()
。
如要進行委派呼叫,請呼叫 DevicePolicyManager.setDelegatedScopes()
,並在範圍引數中加入 DELEGATION_KEEP_UNINSTALLED_PACKAGES
。以下範例說明如何將其他應用程式設為委派項目:
Kotlin
var delegatePackageName = "com.example.tools.kept_app_assist" // Check that the package is installed before delegating. try { context.packageManager.getPackageInfo(delegatePackageName, 0) dpm.setDelegatedScopes( adminName, delegatePackageName, listOf(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES)) } catch (e: PackageManager.NameNotFoundException) { // The delegate app isn't installed. Send a report to the IT admin ... }
Java
String delegatePackageName = "com.example.tools.kept_app_assist"; // Check that the package is installed before delegating. try { context.getPackageManager().getPackageInfo(delegatePackageName, 0); dpm.setDelegatedScopes( adminName, delegatePackageName, Arrays.asList(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES)); } catch (PackageManager.NameNotFoundException e) { // The delegate app isn't installed. Send a report to the IT admin ... }
如果一切正常,委派應用程式會收到 ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED
廣播,並成為委派項目。應用程式可以呼叫本指南中的方法,就像是裝置擁有者或設定檔擁有者一樣。呼叫 DevicePolicyManager
方法時,委派作業會針對管理員元件引數傳遞 null
。
安裝應用程式套件
有時候,在專用裝置上安裝本機快取的自訂應用程式會相當實用。舉例來說,專用裝置通常會部署到限制頻寬的環境或區域,不需要任何網際網路連線。您專用的裝置解決方案應考量客戶的頻寬。您的應用程式可以使用 PackageInstaller
類別開始安裝其他應用程式套件 (APK)。
儘管任何應用程式都可以安裝 APK,但管理員在全代管裝置上都可以安裝 (或解除安裝) 套件,不必與使用者互動。管理員可能會管理裝置、關聯的次要使用者,或相關聯的工作資料夾。安裝完成後,系統會發布通知,讓所有裝置使用者都能看到。通知會通知裝置使用者,瞭解他們的管理員已安裝 (或更新) 應用程式。
Android 版本 | 用於安裝及解除安裝的管理員元件 |
---|---|
Android 9.0 (API 級別 28) 以上版本 | 在全代管裝置上相關聯的次要使用者和工作資料夾 |
Android 6.0 (API 級別 23) 以上版本 | 完全受管理的裝置 |
將 APK 的一或多個複本發布到專用裝置的方式取決於裝置的遠端方式,以及裝置之間的距離。您的解決方案必須遵循安全性最佳做法,才能將 APK 安裝到專用裝置上。
您可以使用 PackageInstaller.Session
建立工作階段,並將一或多個 APK 排入安裝佇列。在下方範例中,我們會收到活動 (singleTop 模式) 的狀態意見回饋,但您可以使用服務或廣播接收器:
Kotlin
// First, create a package installer session. val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL) val sessionId = packageInstaller.createSession(params) val session = packageInstaller.openSession(sessionId) // Add the APK binary to the session. The APK is included in our app binary // and is read from res/raw but file storage is a more typical location. // The I/O streams can't be open when installation begins. session.openWrite("apk", 0, -1).use { output -> getContext().resources.openRawResource(R.raw.app).use { input -> input.copyTo(output, 2048) } } // Create a status receiver to report progress of the installation. // We'll use the current activity. // Here we're requesting status feedback to our Activity but this can be a // service or broadcast receiver. val intent = Intent(context, activity.javaClass) intent.action = "com.android.example.APK_INSTALLATION_ACTION" val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val statusReceiver = pendingIntent.intentSender // Start the installation. Because we're an admin of a fully managed device, // there isn't any user interaction. session.commit(statusReceiver)
Java
// First, create a package installer session. PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); // Add the APK binary to the session. The APK is included in our app binary // and is read from res/raw but file storage is a more typical location. try ( // These I/O streams can't be open when installation begins. OutputStream output = session.openWrite("apk", 0, -1); InputStream input = getContext().getResources().openRawResource(R.raw.app); ) { byte[] buffer = new byte[2048]; int n; while ((n = input.read(buffer)) >= 0) { output.write(buffer, 0, n); } } // Create a status receiver to report progress of the installation. // We'll use the current activity. // Here we're requesting status feedback to our Activity but this can be a // service or broadcast receiver. Intent intent = new Intent(context, getActivity().getClass()); intent.setAction("com.android.example.APK_INSTALLATION_ACTION"); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); IntentSender statusReceiver = pendingIntent.getIntentSender(); // Start the installation. Because we're an admin of a fully managed device, // there isn't any user interaction. session.commit(statusReceiver);
工作階段使用意圖傳送有關安裝的狀態意見回饋。請查看每個意圖的 EXTRA_STATUS
欄位,以便取得 status。請注意,管理員不會收到 STATUS_PENDING_USER_ACTION
狀態更新,因為裝置使用者不需要核准安裝作業。
如要解除安裝應用程式,可以呼叫 PackageInstaller.uninstall
。
全代管裝置、使用者和工作資料夾的管理員都可以解除安裝套件,不必與執行支援的 Android 版本互動的使用者互動 (請參閱表 2)。
凍結系統更新
Android 裝置會接收系統和應用程式軟體的無線更新 (OTA)。如要在關鍵期間 (例如假日或其他忙碌時間) 凍結 OS 版本,專用裝置可能會暫停 OTA 系統更新,最長可達 90 天。詳情請參閱管理系統更新。
遠端設定
Android 的受管理設定可讓 IT 管理員從遠端設定應用程式。您可能需要公開許可清單、網路主機或內容網址等設定,讓應用程式更適合 IT 管理員使用。
如果您的應用程式公開了設定,請記得在說明文件中納入相關設定。如要進一步瞭解如何公開應用程式的設定,以及如何回應設定的變更,請參閱「調整受管理的設定」。
開發設定
當您為專用裝置開發解決方案時,有時將應用程式設為全代管裝置管理員但不恢復原廠設定。如要設定全代管裝置的管理員,請按照下列步驟操作:
- 在裝置上安裝並安裝裝置政策控制器 (DPC) 應用程式。
- 確認裝置上沒有帳戶。
在 Android Debug Bridge (adb) 殼層中執行下列指令。您需要將範例中的
com.example.dpc/.MyDeviceAdminReceiver
替換成應用程式的管理員元件名稱:adb shell dpm set-device-owner com.example.dpc/.MyDeviceAdminReceiver
為協助客戶部署您的解決方案,請參閱其他註冊方法。建議針對特定裝置進行 QR code 註冊。
其他資源
如要進一步瞭解專用裝置,請參閱下列文件: