Książka kucharska na temat urządzeń dedykowanych

Ta książka kucharska pomaga programistom i integratorom systemów ulepszać dedykowane urządzenia. Postępuj zgodnie z naszymi instrukcjami, aby dowiedzieć się, jak dostosować działanie urządzeń do konkretnych wymagań. Ta książka kucharska sprawdzi się najlepiej w przypadku deweloperów, którzy mają już dedykowaną aplikację na urządzenie. Jeśli dopiero zaczynasz, przeczytaj Omówienie urządzeń specjalnych.

Niestandardowe aplikacje ekranu głównego

Te przepisy są przydatne, gdy tworzysz aplikację, która ma zastąpić ekran główny i Menu z aplikacjami na Androidzie.

Aplikacja ekranu głównego

Możesz ustawić aplikację jako aplikację ekranu głównego na urządzeniu, aby była uruchamiana automatycznie przy włączaniu urządzenia. Możesz też włączyć przycisk ekranu głównego, aby umieścić na pierwszym planie aplikację z listy dozwolonych w trybie blokady.

Wszystkie aplikacje ekranu głównego obsługują kategorię intencji CATEGORY_HOME. W ten sposób system rozpoznaje aplikację ekranu głównego. Aby ustawić ją jako domyślną, ustaw jedną z aktywności w aplikacji jako preferowany moduł obsługi intencji, wywołując metodę DevicePolicyManager.addPersistentPreferredActivity() zgodnie z tym przykładem:

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

Nadal musisz zadeklarować filtr intencji w pliku manifestu aplikacji w sposób podany w tym fragmencie 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>

Zwykle nie chcesz, aby aplikacja uruchamiająca wyświetlała się na ekranie Przegląd. Nie musisz jednak dodawać excludeFromRecents do deklaracji aktywności, ponieważ Menu z aplikacjami na Androidzie ukrywa początkowo uruchomioną aktywność, gdy system działa w trybie blokady.

Pokaż oddzielne zadania

FLAG_ACTIVITY_NEW_TASK może być przydatną flagą w przypadku aplikacji typu Menu z aplikacjami, ponieważ każde nowe zadanie wyświetla się jako osobna pozycja na ekranie Przegląd. Więcej informacji o zadaniach na ekranie Przegląd znajdziesz w sekcji Ekran Ostatnie.

Kioski publiczne

Te przepisy świetnie sprawdzają się w przypadku korzystania z urządzeń bez nadzoru w miejscach publicznych, ale mogą też pomóc wielu użytkownikom tych urządzeń skupić się na swoich zadaniach.

Zablokuj urządzenie

Aby mieć pewność, że urządzenia są używane zgodnie z ich przeznaczeniem, możesz dodać ograniczenia dotyczące użytkowników wymienione w tabeli 1.

Tabela 1 Ograniczenia dotyczące użytkowników urządzeń kiosku
Ograniczenie dotyczące użytkownika Opis
DISALLOW_FACTORY_RESET Uniemożliwia użytkownikowi przywrócenie ustawień fabrycznych na urządzeniu. To ograniczenie mogą ustawić administratorzy w pełni zarządzanych urządzeń i użytkownik główny.
DISALLOW_SAFE_BOOT Zapobiega uruchamianiu urządzenia przez użytkownika w trybie awaryjnym, w którym system nie uruchamia automatycznie Twojej aplikacji. To ograniczenie mogą ustawić administratorzy w pełni zarządzanych urządzeń i użytkownik główny.
DISALLOW_MOUNT_PHYSICAL_MEDIA Uniemożliwia użytkownikowi podłączenie woluminów pamięci, które mogą być podłączone do urządzenia. To ograniczenie mogą ustawić administratorzy urządzeń w pełni zarządzanych i użytkownik główny.
DISALLOW_ADJUST_VOLUME Wycisza urządzenie i uniemożliwia użytkownikowi zmianę ustawień głośności dźwięku i wibracji. Sprawdź, czy kiosk nie wymaga dźwięku do odtwarzania multimediów i ułatwień dostępu. To ograniczenie mogą ustawić administratorzy urządzeń w pełni zarządzanych, użytkownika głównego, użytkowników dodatkowych i profili służbowych.
DISALLOW_ADD_USER Uniemożliwia użytkownikowi urządzenia dodawanie nowych użytkowników, w tym użytkowników dodatkowych lub użytkowników z ograniczonym dostępem. System automatycznie dodaje to ograniczenie użytkownika do w pełni zarządzanych urządzeń, ale mogło ono zostać wyczyszczone. To ograniczenie mogą ustawić administratorzy w pełni zarządzanych urządzeń i użytkownik główny.

Ten fragment kodu pokazuje, jak ustawić ograniczenia:

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

Możesz usunąć te ograniczenia, gdy aplikacja jest w trybie administratora, aby administrator IT mógł nadal korzystać z tych funkcji do konserwacji urządzenia. Aby usunąć ograniczenie, wywołaj metodę DevicePolicyManager.clearUserRestriction().

Blokuj okna błędów

W niektórych środowiskach, takich jak pokazy handlowe lub wyświetlanie informacji publicznych, możesz nie chcieć wyświetlać użytkownikom okien z błędami. W Androidzie 9.0 (poziom interfejsu API 28) lub nowszym możesz blokować okna błędów systemu w przypadku awarii lub niereagowania aplikacji, dodając ograniczenie użytkownika DISALLOW_SYSTEM_ERROR_DIALOGS. System uruchomi się ponownie, które nie odpowiadają, tak jakby użytkownik zamknął aplikację w oknie dialogowym. Poniższy przykład pokazuje, jak to zrobić:

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

Jeśli administrator użytkownika głównego lub dodatkowego ustawi to ograniczenie, system wyłączy okna błędów tylko dla tego użytkownika. Jeśli administrator w pełni zarządzanego urządzenia ustawi to ograniczenie, system zablokuje okna wszystkim użytkownikom.

Nie wyłączaj ekranu

Jeśli tworzysz kiosk, możesz zatrzymać uśpienie urządzenia, na którym będzie uruchomiona aktywność w Twojej aplikacji. Dodaj flagę układu FLAG_KEEP_SCREEN_ON do okna aplikacji, jak pokazano w tym przykładzie:

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

Sprawdź, czy urządzenie jest podłączone do ładowarki AC, USB lub bezprzewodowej. Zarejestruj komunikaty dotyczące zmiany baterii i użyj wartości BatteryManager, aby wykryć stan ładowania. Możesz nawet wysłać alerty zdalne do administratora IT, jeśli urządzenie zostanie odłączone. Szczegółowe instrukcje znajdziesz w artykule Monitorowanie poziomu baterii i stanu ładowania.

Możesz też skonfigurować globalne ustawienie STAY_ON_WHILE_PLUGGED_IN tak, aby urządzenie pozostawało w stanie uśpienia, gdy jest podłączone do źródła zasilania. Administratorzy w pełni zarządzanych urządzeń z Androidem 6.0 (poziom interfejsu API 23) lub nowszym mogą wywołać funkcję DevicePolicyManager.setGlobalSetting(), jak pokazano w tym przykładzie:

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

Pakiety aplikacji

Ta sekcja zawiera przepisy na skuteczne instalowanie aplikacji na przeznaczonych do tego urządzeniach.

Buforuj pakiety aplikacji

Jeśli użytkownicy tego samego urządzenia korzystają ze wspólnego zestawu aplikacji, warto w miarę możliwości nie pobierać aplikacji. Aby usprawnić obsługę administracyjną użytkowników na współdzielonych urządzeniach ze stałym zestawem użytkowników, np. na urządzeniach dla pracowników Shift, w Androidzie 9.0 (poziom interfejsu API 28) lub nowszym możesz zapisywać w pamięci podręcznej pakiety aplikacji (APK) potrzebne podczas sesji z wieloma użytkownikami.

Instalacja pliku APK z pamięci podręcznej (zainstalowanego już na urządzeniu) przebiega w 2 etapach:

  1. Komponent administracyjny urządzenia w pełni zarządzanego urządzenia (lub przedstawiciela – patrz niżej) ustawia listę plików APK, które mają być przechowywane na urządzeniu.
  2. Administratorzy powiązanych użytkowników dodatkowych (lub ich przedstawicieli) mogą instalować pakiet APK z pamięci podręcznej w imieniu użytkownika. W razie potrzeby administratorzy w pełni zarządzanego urządzenia, użytkownika głównego lub powiązanego profilu służbowego (albo ich przedstawicieli) mogą też zainstalować aplikację z pamięci podręcznej.

Aby określić listę plików APK, które mają zostać zachowane na urządzeniu, administrator wywołuje metodę DevicePolicyManager.setKeepUninstalledPackages(). Ta metoda nie sprawdza, czy plik APK jest zainstalowany na urządzeniu, co jest przydatne, jeśli chcesz zainstalować aplikację tuż przed tym, jak będzie potrzebna użytkownikowi. Aby uzyskać listę wcześniej skonfigurowanych pakietów, możesz wywołać metodę DevicePolicyManager.getKeepUninstalledPackages(). Po wywołaniu funkcji setKeepUninstalledPackages() ze zmianami lub usunięciu użytkownika dodatkowego system usuwa wszystkie zapisane w pamięci podręcznej pliki APK, które nie są już potrzebne.

Aby zainstalować plik APK zapisany w pamięci podręcznej, wywołaj DevicePolicyManager.installExistingPackage(). Ta metoda umożliwia zainstalowanie aplikacji, która została już zapisana w pamięci podręcznej systemu. Aby można było wywołać tę metodę, dedykowane urządzenie (lub użytkownik urządzenia) musi najpierw zainstalować aplikację na urządzeniu.

Ten przykład pokazuje, jak można użyć tych wywołań interfejsu API w przypadku administratora w pełni zarządzanego urządzenia i użytkownika dodatkowego:

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

Przekazywanie dostępu do aplikacji

Do zarządzania buforowaniem aplikacji możesz przekazać innej aplikacji. Możesz to zrobić, aby oddzielić funkcje swojego rozwiązania lub zaoferować administratorom IT możliwość korzystania z własnych aplikacji. Aplikacja z przekazanym dostępem otrzymuje te same uprawnienia co komponent administratora. Na przykład osoba z przekazanym dostępem do aplikacji u administratora dodatkowego użytkownika może wywołać metodę installExistingPackage(), ale nie może wywołać funkcji setKeepUninstalledPackages().

Aby przekazać wywołanie DevicePolicyManager.setDelegatedScopes() i uwzględnić DELEGATION_KEEP_UNINSTALLED_PACKAGES w argumencie zakresy. Ten przykład pokazuje, jak przekazać dostęp do innej aplikacji:

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

Jeśli wszystko pójdzie dobrze, aplikacja z przekazanym dostępem otrzyma transmisję ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED i zostaje jej przedstawicielami. Aplikacja może wywoływać metody opisane w tym przewodniku tak, jakby była właścicielem urządzenia lub profilu. Wywołując metody DevicePolicyManager, osoba, której przekazano dostęp, przekazuje null dla argumentu komponentu administratora.

Instalowanie pakietów aplikacji

Czasami warto zainstalować niestandardową aplikację zapisaną w pamięci podręcznej na specjalnym urządzeniu. Urządzenia specjalne są często wdrażane w środowiskach o ograniczonej przepustowości lub obszarach bez połączenia z internetem. Wyznaczone przez Ciebie rozwiązanie powinno uwzględniać przepustowość łącza Twoich klientów. Aplikacja może rozpocząć instalację innego pakietu aplikacji (APK) za pomocą klas PackageInstaller.

Pliki APK można instalować w dowolnej aplikacji, ale administratorzy na w pełni zarządzanych urządzeniach mogą instalować (lub odinstalowywać) pakiety bez udziału użytkownika. Administrator może zarządzać urządzeniem, powiązanym użytkownikiem dodatkowym lub powiązanym profilem służbowym. Po zakończeniu instalacji system publikuje powiadomienie, które wyświetla się wszystkim użytkownikom urządzenia. Informuje ono użytkowników urządzeń, że aplikacja została zainstalowana (lub zaktualizowana) przez administratora.

Tabela 2 Wersje Androida obsługujące instalację pakietów bez interakcji z użytkownikiem
Wersja Androida Komponent administratora do instalowania i odinstalowywania
Android 9.0 (poziom interfejsu API 28) lub nowszy powiązanych użytkowników dodatkowych i profili służbowych – zarówno na w pełni zarządzanych urządzeniach,
Android 6.0 (poziom interfejsu API 23) lub nowszy W pełni zarządzane urządzenia

Sposób rozpowszechniania jednej lub kilku kopii pliku APK na dedykowanych urządzeniach zależy od tego, jak oddalone są te urządzenia i jak daleko od siebie znajdują się poszczególne urządzenia. Rozwiązanie musi być zgodne ze sprawdzonymi metodami przed zainstalowaniem plików APK na odpowiednich urządzeniach.

Za pomocą PackageInstaller.Session możesz utworzyć sesję, która umieszcza w kolejce co najmniej 1 plik APK do instalacji. W poniższym przykładzie otrzymujemy opinię o stanie w ramach naszej aktywności (tryb singleTop), ale możesz też użyć usługi lub odbiornika:

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

Sesja wysyła opinię o stanie instalacji za pomocą intencji. Sprawdź pole EXTRA_STATUS każdej intencji, aby poznać stan. Pamiętaj, że administratorzy nie otrzymają aktualizacji stanu STATUS_PENDING_USER_ACTION, ponieważ użytkownik urządzenia nie musi zatwierdzać instalacji.

Aby odinstalować aplikacje, zadzwoń pod numer PackageInstaller.uninstall. Administratorzy w pełni zarządzanych urządzeń, użytkowników i profili służbowych mogą odinstalowywać pakiety bez interakcji użytkownika z obsługiwanymi wersjami Androida (patrz tabela 2).

Zablokuj aktualizacje systemu

Urządzenia z Androidem otrzymują bezprzewodowe aktualizacje systemu i aplikacji. Aby zablokować wersję systemu operacyjnego w okresach krytycznych, takich jak święta lub inne zatłoczenie, dedykowane urządzenia mogą zawiesić aktualizacje systemu OTA na maksymalnie 90 dni. Więcej informacji znajdziesz w artykule Zarządzanie aktualizacjami systemu.

Zdalna konfiguracja

Konfiguracje zarządzane Androida umożliwiają administratorom IT zdalne konfigurowanie aplikacji. Warto udostępnić takie ustawienia jak listy dozwolonych, hosty sieci lub adresy URL treści, aby zwiększyć przydatność aplikacji dla administratorów IT.

Jeśli aplikacja ujawnia swoją konfigurację, pamiętaj o dodaniu ustawień do dokumentacji. Więcej informacji o udostępnianiu konfiguracji aplikacji i reagowaniu na zmiany w ustawieniach znajdziesz w artykule Konfigurowanie konfiguracji zarządzanych.

Konfiguracja programistyczna

Podczas programowania rozwiązania na urządzenia specjalne warto ustawić aplikację jako administratora w pełni zarządzanego urządzenia bez przywracania ustawień fabrycznych. Aby ustawić administratora w pełni zarządzanego urządzenia, wykonaj te czynności:

  1. Skompiluj i zainstaluj aplikację kontrolera zasad dotyczących urządzeń (DPC) na urządzeniu.
  2. Sprawdź, czy na urządzeniu nie ma żadnych kont.
  3. Uruchom to polecenie w powłoce Android Debug Bridge (adb). Musisz zastąpić w przykładzie ciąg com.example.dpc/.MyDeviceAdminReceiver nazwą komponentu administratora aplikacji:

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

Aby pomóc klientom wdrożyć Twoje rozwiązanie, musisz zapoznać się z innymi metodami rejestracji. W przypadku dedykowanych urządzeń zalecamy rejestrację za pomocą kodu QR.

Dodatkowe materiały

Aby dowiedzieć się więcej o dedykowanych urządzeniach, przeczytaj te dokumenty: