專用裝置教戰手冊

本教戰手冊可協助開發人員和系統整合商強化對 裝置解決方案參考我們的步驟食譜,找出適合這些裝置的解決方案 行為本教戰手冊最適合已 如果您還不熟悉裝置應用程式,請參閱專用裝置。 總覽頁面

自訂 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 列出的使用者限制。

表 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 人員 管理員查看。如需逐步操作說明,請參閱 監控電池電量和充電 State

您也可以設定 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 階段

  1. 全代管裝置 (或委派代表) 的管理員元件。請參閱 以下) 設定要在裝置上保留的 APK 清單。
  2. 關聯次要使用者 (或其委派代表) 的管理員元件 代表使用者安裝快取的 APK。全代管環境的管理員 裝置、主要使用者或關聯工作資料夾 ( 視需要安裝快取的應用程式。

如要設定在裝置上保留的 APK 清單,管理員會呼叫 DevicePolicyManager.setKeepUninstalledPackages()。 這個方法不會檢查 APK 是否已安裝在裝置上。如果您 就可以先安裝應用程式若要取得 先前設定的所有套件,只要呼叫 DevicePolicyManager.getKeepUninstalledPackages()。 變更 setKeepUninstalledPackages() 並進行變更或建立次要執行個體時 使用者的刪除動作時,系統會刪除任何不再需要的快取 APK。

如要安裝快取的 APK,請呼叫 DevicePolicyManager.installExistingPackage()。 這種方法只能安裝系統快取過的應用程式,也就是 專用裝置解決方案 (或裝置的使用者) 必須先將應用程式安裝在 才能呼叫這個方法

以下範例顯示如何在「管理員」頁面 全代管裝置和次要使用者:

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() 和 include 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,但全代管裝置上的管理員可以 在使用者沒有進行互動的情況下,直接安裝 (或解除安裝) 套件。管理員可能會控管 關聯的次要使用者或是關聯的工作資料夾更新後 完成安裝後,系統會發布通知讓所有裝置使用者 請參閱這則通知會告知裝置使用者,應用程式已經安裝 (或 更新)。

表 2.支援套件安裝的 Android 版本 沒有使用者互動
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 人員提供更實用的應用程式 管理員。

如果您的應用程式會公開設定,請記得在您的 說明文件。進一步瞭解如何公開應用程式設定及回應 變更相關設定,請參閱「調整受管理的設定」。

開發設定

為專用裝置開發解決方案時, 很方便,將應用程式設為完全受管理裝置的管理員 (無工廠) 重設。如要設定全代管裝置的管理員,請按照下列步驟操作:

  1. 在裝置上安裝及安裝裝置政策控制器 (DPC) 應用程式。
  2. 確認裝置上沒有帳戶。
  3. Android Debug Bridge (ADB) 殼層中執行下列指令。個人中心 您需要將範例中的 com.example.dpc/.MyDeviceAdminReceiver 替換成 應用程式的管理員元件名稱:

    adb shell dpm set-device-owner com.example.dpc/.MyDeviceAdminReceiver

為了協助客戶部署您的解決方案,您必須考慮其他報名服務 方法。我們建議開發人員註冊 QR code, 以及專用裝置

其他資源

如要進一步瞭解專用裝置,請參閱下列文件: