Este manual ajuda desenvolvedores e integradores de sistemas a melhorar a solução de dispositivo dedicada. Siga nossas receitas para encontrar soluções para comportamentos de dispositivos dedicados. Este livro de receitas funciona melhor para desenvolvedores que já têm um app de dispositivo dedicado. Se você está apenas começando, leia Visão geral de dispositivos dedicados.
Apps personalizados do Google Home
Essas receitas são úteis se você estiver desenvolvendo um app para substituir a tela inicial e o acesso rápido do Android.
Seja o app de início
É possível configurar seu app como o app de início do dispositivo para que ele seja iniciado automaticamente quando o dispositivo for iniciado. Você também pode ativar o botão home, que coloca o app na lista de permissões em primeiro plano no modo de bloqueio de tarefas.
Todos os apps de casa processam a categoria de intent CATEGORY_HOME
. É assim
que o sistema reconhece um app de início. Para se tornar o app de início padrão, defina uma
das atividades do app como o gerenciador de intent preferido da página inicial, chamando
DevicePolicyManager.addPersistentPreferredActivity()
,
conforme mostrado no exemplo a seguir:
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);
Ainda é necessário declarar o filtro de intent no arquivo de manifesto do app, conforme mostrado no snippet XML a seguir:
<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>
Normalmente, você não quer que o app de acesso rápido apareça na tela "Visão geral".
No entanto, não é necessário adicionar excludeFromRecents
à
declaração de atividade, porque a tela de início do Android oculta a atividade iniciada
inicialmente quando o sistema está em execução no modo de bloqueio de tarefas.
Mostrar tarefas separadas
A FLAG_ACTIVITY_NEW_TASK
pode ser uma sinalização útil para
apps do tipo de tela de início, porque cada nova tarefa aparece como um item separado na
tela "Visão geral". Para saber mais sobre tarefas na tela "Visão geral", leia Tela "Recentes".
Quiosques públicos
Essas receitas são ótimas para dispositivos autônomos em espaços públicos, mas também podem ajudar muitos usuários de dispositivos dedicados a se concentrar nas tarefas.
Bloquear o dispositivo
Para garantir que os dispositivos sejam usados para a finalidade pretendida, adicione as restrições de usuário listadas na Tabela 1.
Restrição de usuário | Descrição |
---|---|
DISALLOW_FACTORY_RESET |
Impede que um usuário redefina o dispositivo para a configuração original. Os administradores de dispositivos totalmente gerenciados e o usuário principal podem definir essa restrição. |
DISALLOW_SAFE_BOOT |
Impede que um usuário inicie o dispositivo no modo de segurança, o que impede a inicialização automática do app pelo sistema. Os administradores de dispositivos totalmente gerenciados e o usuário principal podem definir essa restrição. |
DISALLOW_MOUNT_PHYSICAL_MEDIA |
Impede que o usuário do dispositivo monte volumes de armazenamento que possam ser conectados ao dispositivo. Os administradores de dispositivos totalmente gerenciados e o usuário principal podem definir essa restrição. |
DISALLOW_ADJUST_VOLUME |
Silencia o dispositivo e impede que o usuário mude as configurações de volume de som e vibração. Verifique se o quiosque não precisa de áudio para reprodução de mídia ou recursos de acessibilidade. Os administradores de dispositivos totalmente gerenciados, o usuário principal, os usuários secundários e os perfis de trabalho podem definir essa restrição. |
DISALLOW_ADD_USER |
Impede que o usuário do dispositivo adicione novos usuários, como secundários ou restritos. O sistema adiciona automaticamente essa restrição de usuário a dispositivos totalmente gerenciados, mas ela pode ter sido removida. Os administradores de dispositivos totalmente gerenciados e o usuário principal podem definir essa restrição. |
O snippet a seguir mostra como definir as restrições:
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);
Você pode remover essas restrições quando o app estiver no modo de administrador para que um administrador de TI ainda possa usar esses recursos para a manutenção do dispositivo. Para limpar a restrição, chame DevicePolicyManager.clearUserRestriction()
.
Suprimir caixas de diálogo de erro
Em alguns ambientes, como demonstrações na loja ou exibições de informações públicas, talvez você não queira mostrar caixas de diálogo de erro aos usuários. No Android 9.0 (API
de nível 28) ou versões mais recentes, é possível suprimir caixas de diálogo de erro do sistema para apps com falha
ou que não respondem adicionando a
restrição de usuário
DISALLOW_SYSTEM_ERROR_DIALOGS
. O sistema reinicia apps que não respondem como se o usuário do dispositivo tivesse fechado
o app na caixa de diálogo. O exemplo a seguir mostra como fazer isso:
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); }
Se um administrador do usuário principal ou secundário definir essa restrição, o sistema suprime as caixas de diálogo de erro apenas para esse usuário. Se um administrador de um dispositivo totalmente gerenciado definir essa restrição, o sistema vai suprimir as caixas de diálogo de todos os usuários.
Manter a tela ativada
Se você estiver construindo um quiosque, poderá interromper um dispositivo que entrará
em suspensão quando ele estiver executando a atividade do app. Adicione
a sinalização de layout FLAG_KEEP_SCREEN_ON
à janela
do app, conforme mostrado no exemplo a seguir:
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); }
Confira se o dispositivo está conectado a um carregador CA, USB ou
sem fio. Registre-se para transmissões de mudança de bateria e use valores de BatteryManager
para descobrir o estado de carregamento. É possível até enviar alertas remotos para um administrador
de TI se o dispositivo ficar desconectado. Para instruções passo a passo, leia
Monitorar o nível da bateria e o estado de
carregamento.
Você também pode definir a configuração global STAY_ON_WHILE_PLUGGED_IN
para manter o dispositivo ativado enquanto estiver conectado a uma fonte de energia.
Os administradores de dispositivos totalmente gerenciados no Android 6.0 (API de nível 23) ou versões mais recentes podem
chamar DevicePolicyManager.setGlobalSetting()
, conforme mostrado
neste exemplo:
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));
Pacotes de apps
Esta seção contém roteiros para instalar apps de forma eficiente em dispositivos dedicados.
Armazenar pacotes de apps em cache
Se os usuários de um dispositivo compartilhado compartilham um conjunto comum de apps, faz sentido evitar o download de apps sempre que possível. Para simplificar o provisionamento de usuários em dispositivos compartilhados com um conjunto fixo de usuários, como dispositivos para funcionários de turno, no Android 9.0 (API de nível 28) ou versões mais recentes, é possível armazenar em cache os pacotes de apps (APKs) necessários para sessões multiusuário.
A instalação de um APK armazenado em cache (que já está instalado no dispositivo) acontece em duas etapas:
- O componente de administrador de um dispositivo totalmente gerenciado (ou um delegado, veja a seguir) define a lista de APKs a serem mantidos no dispositivo.
- Os componentes de administrador de usuários secundários afiliados (ou delegados) podem instalar o APK armazenado em cache em nome do usuário. Os administradores do dispositivo totalmente gerenciado, o usuário principal ou um perfil de trabalho afiliado (ou os delegados) também podem instalar o app armazenado em cache, se necessário.
Para definir a lista de APKs que vão ser mantidos no dispositivo, o administrador chama
DevicePolicyManager.setKeepUninstalledPackages()
.
Esse método não verifica se o APK está instalado no dispositivo. Isso é útil se você
quiser instalar um app pouco antes de precisar dele para um usuário. Para ver uma lista de
pacotes definidos anteriormente, chame
DevicePolicyManager.getKeepUninstalledPackages()
.
Depois de chamar setKeepUninstalledPackages()
com mudanças ou quando um usuário
secundário é excluído, o sistema exclui todos os APKs armazenados em cache que não são mais necessários.
Para instalar um APK armazenado em cache, chame
DevicePolicyManager.installExistingPackage()
.
Esse método só pode instalar um app que o sistema já tenha armazenado em cache. Sua
solução de dispositivo dedicada (ou o usuário de um dispositivo) precisa primeiro instalar o app
no dispositivo antes que você possa chamar esse método.
O exemplo a seguir mostra como você pode usar essas chamadas de API no administrador de um dispositivo totalmente gerenciado e um usuário secundário:
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);
Delegar apps
Você pode delegar o gerenciamento do armazenamento em cache de outro app. Isso pode ser feito para
separar os recursos da solução ou oferecer a capacidade de administradores de TI usarem
os próprios aplicativos. O app delegado recebe as mesmas permissões que o componente
administrador. Por exemplo, um delegado do app de um administrador de usuário secundário pode chamar
installExistingPackage()
, mas não pode chamar setKeepUninstalledPackages()
.
Para fazer uma chamada delegada
DevicePolicyManager.setDelegatedScopes()
e incluir
DELEGATION_KEEP_UNINSTALLED_PACKAGES
no argumento de escopos. O exemplo abaixo mostra como você pode tornar outro app
o delegado:
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 ... }
Se tudo correr bem, o app delegado receberá a transmissão
ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED
e se tornará o delegado. O app pode chamar os métodos apresentados neste guia
como se fosse o proprietário do dispositivo ou do perfil. Ao chamar
métodos DevicePolicyManager
, o delegado passa null
para o argumento do componente
de administrador.
Instalar pacotes de apps
Às vezes, é útil instalar um app personalizado armazenado em cache localmente em um dispositivo
dedicado. Por exemplo, dispositivos dedicados são frequentemente implantados em
ambientes com largura de banda limitada ou em áreas sem conexão de Internet. Sua
solução de dispositivo dedicada precisa considerar a largura de banda dos seus clientes. O
app pode iniciar a instalação de outro pacote de apps (APK) usando as
classes PackageInstaller
.
Embora qualquer app possa instalar APKs, os administradores em dispositivos totalmente gerenciados podem instalar (ou desinstalar) pacotes sem interação do usuário. O administrador pode gerenciar o dispositivo, um usuário secundário afiliado ou um perfil de trabalho afiliado. Depois de concluir a instalação, o sistema publica uma notificação que aparece para todos os usuários do dispositivo. A notificação informa aos usuários do dispositivo que o app foi instalado (ou atualizado) pelo administrador.
Versão do Android | Componente "Administrador" para instalação e desinstalação |
---|---|
Android 9.0 (API de nível 28) ou mais recente | Usuários secundários afiliados e perfis de trabalho, ambos em dispositivos totalmente gerenciados |
Ter o Android 6.0 (API de nível 23) ou versões mais recentes | Dispositivos totalmente gerenciados |
A forma como você distribui uma ou mais cópias do APK para dispositivos dedicados depende da distância entre os dispositivos e, possivelmente, da distância entre eles. Sua solução precisa seguir as práticas recomendadas de segurança antes de instalar APKs em dispositivos dedicados.
Use PackageInstaller.Session
para criar uma sessão que enfileira um
ou mais APKs para instalação. No exemplo a seguir, recebemos feedback de status
em nossa atividade (modo singleTop), mas você pode usar um
serviço ou broadcast receiver:
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);
A sessão envia feedback de status sobre a instalação usando intents. Verifique
o campo EXTRA_STATUS
de cada intent para saber o
status. Lembre-se de que os administradores não recebem a
atualização de status STATUS_PENDING_USER_ACTION
porque o usuário do dispositivo não precisa aprovar a instalação.
Para desinstalar apps, chame PackageInstaller.uninstall
.
Os administradores de dispositivos, usuários e perfis de trabalho totalmente gerenciados podem desinstalar pacotes
sem interação do usuário executando versões do Android com suporte. Consulte a
Tabela 2.
Congelar atualizações do sistema
Os dispositivos Android recebem atualizações over the air (OTA) para o software do sistema e do aplicativo. Para congelar a versão do SO em períodos críticos, como feriados ou outros horários de pico, os dispositivos dedicados podem suspender as atualizações do sistema OTA por até 90 dias. Para saber mais, leia Gerenciar atualizações do sistema.
Configuração remota
As configurações gerenciadas do Android permitem que os administradores de TI configurem seu app remotamente. É recomendável expor configurações como listas de permissões, hosts de rede ou URLs de conteúdo para tornar o app mais útil para os administradores de TI.
Se o app expõe a própria configuração, lembre-se de incluí-las na documentação. Para saber mais sobre como expor as configurações do app e reagir a mudanças nas configurações, leia Definir configurações gerenciadas.
Configuração de desenvolvimento
Ao desenvolver uma solução para dispositivos dedicados, às vezes é útil configurar seu app como o administrador de um dispositivo totalmente gerenciado sem uma redefinição para a configuração original. Para definir o administrador de um dispositivo totalmente gerenciado, siga estas etapas:
- Crie e instale o app controlador de política de dispositivo (DPC) no dispositivo.
- Verifique se não há contas no dispositivo.
Execute o comando a seguir no shell do Android Debug Bridge (adb). É necessário substituir
com.example.dpc/.MyDeviceAdminReceiver
no exemplo pelo nome do componente de administrador do app:adb shell dpm set-device-owner com.example.dpc/.MyDeviceAdminReceiver
Para ajudar os clientes a implantar a solução, analise outros métodos de registro. Recomendamos o registro de código QR para dispositivos dedicados.
Outros recursos
Para saber mais sobre dispositivos dedicados, leia os seguintes documentos: