Reparar, cortar, cavar. Resolvendo a missão online Droid Mission



No ano passado, realizamos uma busca on-line para desenvolvedores móveis - Missão Droid. Em um mês, os participantes tiveram que resolver o maior número possível de problemas em três direções: conserte! (pesquisa de bugs e pesquisa de código), corte-o! (engenharia reversa) e desenterre-o! (aprendendo os recursos do Android). No total, a missão teve 23 tarefas - elas são muito parecidas com as encontradas pelos especialistas em Android no trabalho real. No post, mostraremos todas as condições e as soluções certas.


Pesquise erros e pesquisa de código


conserte! # 1

Autor: Anastasia Laushkina

Em uma noite tranquila de sexta-feira, dois agentes ficam acordados até tarde e lideram uma disputa insolúvel. Eles foram instruídos a armazenar uma grande quantidade de dados secretos em um banco de dados Sqlite com uma chave de chave inteira.

O agente A cria uma tabela com uma consulta no formato: CREATE TABLE t (chave INT PRIMARY KEY, secret_value_1, secret_value_2) e o agente B com uma consulta no formato CREATE TABLE t (chave INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2).

O Agente A tem certeza absoluta de que não há erro em sua solicitação; no entanto, o Agente B tem um argumento contra isso: ele afirma que a integridade dos dados durante a abordagem do Agente A pode ser violada.

Lista com vírgula: a coluna ou colunas responsáveis ​​pela integridade do Agente B, mas não pelo Agente A; bem como a estrutura de dados, em russo, devido à qual a solicitação do agente B será mais rápida.

Anotações
Formato da resposta: [nome da coluna 1], [nome da coluna N], [nome da estrutura] (sem colchetes, com letras minúsculas)

Exemplo de resposta: x, y, matriz

Solução

É sobre rowId e seu relacionamento com a chave primária.

No caso da chave "CREATE TABLE t (chave INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2)", a chave é um alias para rowId. No caso de "CREATE TABLE t (chave INT PRIMARY KEY, secret_value_1, secret_value_2)" - não. O motivo pode ser encontrado na documentação .

Portanto, na ausência de um relacionamento-chave com o rowId, uma solicitação no formato "INSERIR EM VALORES (" alguns "," y "," z ")" será executada com êxito, potencialmente violando a integridade dos dados. Se houver uma conexão, ocorrerá um erro de incompatibilidade de tipo de dados.

A resposta foi aceita como um par de chave, rowId e key / rowId separadamente (porque, se houver uma conexão, eles se referirão à mesma coluna).

A velocidade é alcançada através do armazenamento na forma de uma árvore de pesquisa.

conserte! # 2

Autor: Anastasia Laushkina
Arquivo binário

Para detectar rapidamente inimigos, o agente Kew está desenvolvendo um aplicativo com uma lista de criminosos conhecidos. Ele conseguiu exibir facilmente essa lista no aplicativo, mas decide que informações importantes devem estar na tela principal.

Para fazer isso, ele precisa de um widget. Um problema: por algum motivo, o widget não exibe uma lista. Um instinto diz ao agente que o problema está apenas em uma linha.

Nomeie-o com uma vírgula, bem como a linha pela qual você precisa substituí-lo, para obter a melhor solução.

Anotações
Formato da resposta: [linha 1], [linha 2] (sem colchetes)

Exemplo de resposta: FrameLayout, ScrollView

Solução

1. Estamos procurando um arquivo de layout para o widget. Vimos nele um elemento ConstraintLayout inválido (consulte developer.android.com/guide/topics/appwidgets#CreatingLayout ).
2. Altere para o aceitável e ideal - LinearLayout / FrameLayout.

Apesar do fato de que o uso do FrameLayout quebrou o layout dos elementos no widget, ele ainda foi aceito como resposta.

conserte! # 3

Autor: Artyom Viter
Arquivo binário

Os testadores notaram um aumento anormal no consumo de RAM. Há uma suspeita de que haja um vazamento em algum lugar.

Você precisa investigar e localizar o problema. Na resposta, indique o nome do método da classe de aplicativo e o local da chamada, o que leva a um vazamento de memória. Se você encontrar vários problemas, liste todos.

Formato da resposta: [Nome da classe na qual o método é chamado]: [número da linha neste arquivo]: [nome do método] (sem colchetes)

Indique várias respostas em ordem lexicográfica, separadas por um #.

Um exemplo:

Se você acha que há um vazamento de memória no arquivo ExampleFileName.java,

class ExampleFileName {   public problemMethodName(Sting arg) {}   public test(Sting arg) {     problemMethodName(arg); //  8   ExampleFileName.java   } } 

Exemplo de resposta: ExampleFileName: 8: problemMethodName

Solução

A primeira maneira é a análise de código.

  1. Estudamos o código do aplicativo.
  2. O primeiro vazamento é óbvio. Este é um registro BroadCastReceiver na classe SecondActivity. Uma mensagem sobre esse problema pode ser vista no logcat se você executar o aplicativo.
  3. Se você prestar atenção ao método startTimer (), notará que a classe CountDownTimer adiciona um fechamento ao manipulador de threads principal com um link para textView. E antes que a atividade seja destruída, a classe de instância (CountDownTimer) não chama o método cancel (). Este é o segundo vazamento de memória.

A segunda maneira são as ferramentas. Você pode implementar a biblioteca de vazamentos no aplicativo, obter heap dump via adb ou android Profiler e analisar os relatórios recebidos. O Eclipse Memory Analyzer (MAT) pode ajudar com a análise. Para abrir o heapdump no MAT, você precisa convertê-lo para um formato compreensível para o MAT usando o utilitário hprof-convd.

conserte! # 4

Publicado por: Vali Ibragimov
Arquivo binário

Ivan tem seu primeiro dia em uma sala de cinema online. Como ele é um desenvolvedor legal, ele foi imediatamente solicitado a corrigir a falha ao assistir a um filme. Ele decidiu ao mesmo tempo refatorar. O código se tornou mais conciso e mais limpo, o erro deixou de ser reproduzido.

No entanto, mais tarde descobriu-se que a posição atual do filme não foi enviada para o back-end. Ajude Ivan a entender qual classe ele misturou.

A resposta é o nome da classe que ela deve usar.

Solução

Para enviar uma posição de visualização a cada cinco segundos e ir para a rede, o ExecutorService é usado. Criando um ScheduledThreadPoolExecutor para uma tarefa periódica, pensou Ivan - por que não usá-lo para acessar a rede? Afinal, ScheduledThreadPoolExecutor é o descendente de ThreadPoolExecutor. Ivan nem suspeitava que o ScheduledThreadPoolExecutor usa apenas um encadeamento e, ao acessar a rede, será necessário aguardar a conclusão da tarefa periódica.

 scheduleFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {                    try {                        timingsApi.sendTiming(filmId, player.getContentPosition()).get();                    } catch (Exception e) {                        e.printStackTrace();                    }                },                0, PERIOD_SECONDS, TimeUnit.SECONDS); 

A solução para o problema ficou ainda mais complicada pelo fato de não estarmos acessando o ExoPlayer no thread da interface do usuário - e na nova versão 2.9. * O ExoPlayer começou a escrever avisos sobre isso nos logs. Mas agora isso é apenas um aviso, e não um erro cometido por Ivan.

conserte! # 5

Autor: Alexander Tsybin
Arquivo binário

Uma empresa estava desenvolvendo um jogo Farmer Simulator.

Neste jogo, apenas um jogador pode se mover livremente. Todos os outros objetos estavam imóveis ou tinham uma trajetória de movimento claramente definida. Em algum momento, eles decidiram adicionar inimigos ao jogo que se tornariam um obstáculo para o jogador.

O desenvolvedor Vasya foi instruído a perceber os inimigos. Durante a implementação, Vasya encontrou um problema de desempenho. Ele decidiu corrigi-lo devido ao multithreading. Como Vasya não conhecia o multithreading, ele aproveitou as soluções do stackoverflow. Então ele se livrou dos problemas de desempenho.

No entanto, na fase de testes, verificou-se que, às vezes, o jogo congela. Ajude Vasya a encontrar o motivo.

Como resposta, insira os números das linhas problemáticas de código - separadas por vírgulas, em ordem crescente e sem espaços. Assim: 1,17,42

Solução

Tipos primitivos de boxe (por exemplo, int em Inteiro) são operações caras. Para fins de otimização, as máquinas Java às vezes reutilizam esses objetos. Linhas não são excepção.

As linhas ENEMY_LOCK e DECISIONS_LOCK são usadas no código para sincronização. Portanto, se houver locais no projeto com sincronização nas mesmas linhas, poderá ocorrer um conflito.

Em geral, você não pode usar String / Inteiro, etc., para sincronização.

Resposta: 7.8

conserte! # 6

Autor: Ivan Pukhov
Arquivo binário

A equipe de processamento de imagens lançou uma nova biblioteca nativa. Você precisa experimentá-la e apontar os problemas, se houver.

Solução

Esta é uma tarefa fácil de aquecer. Nos recursos está a própria biblioteca e o arquivo de cabeçalho para ela. No cabeçalho, a única função retornando um jobject.

Não há mais nada a fazer além de criar um projeto de teste (instalando o NDK) e chamá-lo! Uma chamada de função no thread principal lançará uma exceção pedindo para não chamá-la dessa maneira. Transferimos a execução para outro thread de qualquer forma - e tudo funciona.

Agora vale a pena verificar o objeto retornado - é realmente um bitmap? A maneira mais fácil é exibi-lo imediatamente em um ImageView. Veremos uma imagem com uma sequência de texto, e a tarefa requer uma resposta também na forma de uma sequência. Provavelmente não é apenas uma coincidência. :)

conserte! # 7

Autor: Dmitry Zaitsev
Arquivo binário

Existe uma aplicação com códigos onde o código desejado está enquadrado. Infelizmente, o layout foi quebrado e precisa ser restaurado para encontrar o código.

Anotações
Depois de corrigir o código, o quadro passará exatamente por 22 caracteres que precisam ser listados no sentido anti-horário, começando no canto superior esquerdo do quadro.

Exemplo de resposta: ScQG1jxbazmjbcARefbRMo

Solução

Agora o Dagger se tornou o padrão de fato no desenvolvimento do Android. Quando o DI já está configurado, é muito simples escrever Inject na frente do construtor e dar todo o restante do trabalho para Dagger. É tão fácil que você pode parar de pensar em como as dependências são fornecidas. Também no Android, você frequentemente precisa trabalhar com a classe Context e seus descendentes: Aplicativo e Atividade. O aplicativo está vinculado ao ciclo de vida do aplicativo e a Atividade está vinculada ao ciclo de vida da tela. Portanto, ao trabalhar com essas classes, é importante observar muitos recursos. A tarefa é precisamente baseada no uso do contexto errado, e tudo isso é disfarçado usando DI.

Os seguintes passos podem ser seguidos para resolver:

  1. Examine os arquivos do projeto e observe que ItemStyle é aplicado a todos os TextViews.
  2. Veja que o estilo não é realmente aplicado. Isso deve levar à ideia de que o problema está relacionado ao tópico e, portanto, ao contexto da Atividade.
  3. Entenda o gráfico de dependência e entenda que o contexto do Aplicativo é fornecido no adaptador.
  4. No ContestAdapter, altere o tipo de contexto para Atividade.

conserte! # 8

Publicado por: Vali Ibragimov
Arquivo binário

Ivan recebeu a tarefa de refatorar o aplicativo para obter uma lista de tarefas por meio do ContentProvider usando DI. Ele refatorou o aplicativo usando o Dagger e, quando iniciado, o aplicativo começou a falhar.

Ajude Ivan a entender o motivo:

  1. Especifique o número da linha com o nome da classe que você deseja transferir para outra classe.
  2. Forneça um link para a documentação com uma âncora em um local que o ajude a entender por que o aplicativo falha.

Anotações
Formato de resposta: [Nome da classe]: [Número da linha] -> [Nome da classe]: [Número da linha], [Link]

Exemplo de resposta:

FirstCLass: 12-> SecondClass: 45, https: //developer.android.com/reference/android/app/Activity#activity-lifecycle

Solução

A tarefa consistiu em duas partes:

1. Entenda e forneça uma solução de falha no ContentProvider ao acessar o componente DI. A solução correta foi transferir a inicialização do componente DI para o ContentProvider.



O método onCreate no ContentProvider é chamado mais cedo que no Application. É fácil verificar se você examina o código de início do aplicativo na classe ActivityThread e no método handleBindApplication.



2. Forneça um link descrevendo que onCreate for ContentProvider é chamado mais cedo que para Application. O Google não menciona isso na página de documentação do ContentProvider. Mas aqui está um trecho da descrição do método onCreate Aplication:

Chamado quando o aplicativo está iniciando, antes que qualquer atividade, serviço ou objeto receptor (excluindo provedores de conteúdo) tenha sido criado.

conserte! # 9

Autor: Alexander Tsybin
Arquivo binário

O desenvolvedor recebeu a tarefa de implementar o modo de exibição para obter uma lista de itens. No TK, havia um requisito importante - rolar a lista para o primeiro e o último elementos para que esses elementos apareçam no centro da tela. O desenvolvedor implementou a funcionalidade, mas ao testar, às vezes a centralização não funciona. Ajude o desenvolvedor a encontrar as linhas problemáticas no código.

Anotações
Formato da resposta:

<Nome do arquivo sem extensão>: <Números de linha separados por vírgulas>

<Nome do arquivo sem extensão>: <Números de linha separados por vírgulas>

As linhas devem ser classificadas em ordem lexicográfica. Os números das linhas devem ser classificados em ordem crescente.

Exemplo de resposta:

BarFoo: 1

FooBar: 12,13,14,99

Solução

Existem dois problemas no código:

1. A julgar pelo código, supõe-se que a chamada para onViewDetachedFromWindow sempre ocorra após onBindViewHolder, mas não é assim. O método duplo para onViewDetachedFromWindow é onViewAttachedToWindow. Como resultado, holder.view.reset () é chamado e a centralização desaparece se a rolagem for lenta.

2. No Android anterior à versão 7, o addView (rootView, parâmetros) não funciona da maneira esperada.

Quando você adiciona uma View, um LayoutParam é gerado, com a seguinte aparência:

Marshmallow - github.com/aosp-mirror/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/FrameLayout.java#L403
Nougat - github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/widget/FrameLayout.java#L384

O FrameLayout.LayoutParams possui dois construtores, um dos quais aceita ViewGroup.MarginLayoutParams e copia as margens em si: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L14

O segundo construtor aceita ViewGroup.LayoutParams e ignora as margens: github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L7328

Como resultado, a centralização não funciona no Android até a versão 7.

A resposta é:
Visualização do item: 35
Atividade principal: 53

Engenharia reversa


hackear! # 1

Autor: Valentin Baryshev
Arquivo binário

Um desenvolvedor se apressou e nem testou o .apk final antes de enviar para o concurso.

Aconteceu que ele misturou algum grupo de visualizações em activity_main.xml. Além disso, ele colocou cada letra da chave em uma visualização de texto separada. Como resultado, todas as visualizações no layout se separaram e há uma indignação completa na tela.

Tente determinar qual grupo de visualizações deve estar no lugar dos atuais.

A resposta para o problema é o código que aparece na tela após a substituição do grupo de exibição correto.

Solução

É necessário descompilar .apk de qualquer maneira disponível (por exemplo, por descompiladores online) e recriar o projeto por código.

Se analisarmos main_layout, podemos adivinhar pelos atributos que o grupo de visualizações raiz deve ser ConstraintLayout.

Resposta: tniartsnoc

hackear! # 2

Autor: Alexander Tsybin
Arquivo binário

Um usuário experiente travou o aplicativo. Ele não ficou surpreso, abriu os logs, encontrou a linha que causou o erro: N72XbphDx5NnFl6CKMNl8w == e a enviou ao suporte técnico.

Verificou-se que ele estava usando uma versão desatualizada do aplicativo, que havia sido desenvolvida antes de usar repositórios de código, portanto o código de descriptografia foi perdido. No entanto, foi possível encontrar .apk da versão antiga do aplicativo.

Ajude a decifrar a mensagem.

Solução

É necessário descompilar .apk de qualquer maneira disponível (por exemplo, por descompiladores online) e recriar o projeto por código.

O código em si contém a função de criptografia e a função de descriptografia, à qual você pode fornecer a cadeia criptografada e obter uma resposta.

A resposta é:
TF: Bom trabalho!

hackear! # 3

Autor: Valentin Baryshev
Arquivo binário

Como você conhece o ciclo de vida de um aplicativo Android?

Você sabia que, em alguns casos, o sistema pode interromper o processo de inscrição?

Simule esta situação no aplicativo.

Você verá a resposta após restaurar o aplicativo - na forma de código.

Solução

Foi o suficiente para instalar .apk no telefone, executar e obter o caso quando o sistema descarrega o aplicativo, mas, ao mesmo tempo, permanece na cortina.

Uma opção é instalar o aplicativo, iniciá-lo e minimizá-lo (ou apenas alternar para outro aplicativo). Então nos conectamos via adb ao computador. No Android Studio, selecione a guia Logcat, selecione o processo deste aplicativo e clique no ícone vermelho de parada. Em seguida, abra o aplicativo anteriormente minimizado da cortina. A tela exibirá a resposta: sss2384gxcxxX.

hackear! # 4

Autor: Ivan Pukhov
Arquivo binário

Um colega de front-end publicou uma demonstração do novo módulo, mas esqueceu de enviar as especificações. Agora ele dorme em um fuso horário diferente, e uma demonstração deve ser feita em alguns minutos. Em uma conversa com um colega, você se lembra da frase "Sim, é fácil lá, basta escrever stubs e ele funcionará".

Faça o download do arquivo e tente lidar sem um colega.

Solução

Essa é uma tarefa simples, mas requer certa curiosidade e perseverança.

Faça o download do archive, faça upload de seu conteúdo para o servidor de teste local, crie um projeto com o WebView e execute-o. Após um curto tempo, vemos a mensagem "Calibrar orientação". E é isso. Mas a mensagem sugere que talvez você deva torcer o telefone com as mãos? Depois disso, uma mensagem suspeita sobre a exceção lançada será exibida - com uma chamada para verificar a saída do console.

Abrimos o console e vemos uma chamada para um método inexistente no objeto API. Adicionamos um objeto ao WebView para implementar a API JS e novamente tentamos testar o aplicativo.

Aparentemente, vale a pena adicionar um valor de retorno. Nós selecionamos, adicionamos - funciona. Mas não há linha com a resposta em lugar algum. Ligue o telefone novamente - um novo método é chamado. Adicione-o também. Após várias iterações, obtemos a chamada finalizeCalibrating e, em seguida, storeResult, na qual nossa linha chegará.

Ao baixar a fonte, talvez você não queira resolver nada, mas abra-a e retire a resposta. Esse método funciona, mas alguns pequenos ancinhos esperam por você ao longo do caminho.

hackear! # 5

Autor: Ivan Pukhov

A montagem do desenvolvedor da nova versão do android vazou para a rede, na qual o novo mecanismo de assinatura do aplicativo foi depurado diretamente no dispositivo. Nesse assembly, a chave privada para assinatura é costurada no código-fonte do sistema, e os desenvolvedores deixaram partes da interface do usuário para recebê-lo. Encontre a chave.


Solução

1. Ao executar o emulador, você notará o aplicativo SecretActivity. Se você abrir, um brinde será exibido, oferecendo a visualização dos logs. Como esse é um assembly de depuração, há muitos logs - eles precisam ser filtrados pelo nome do aplicativo.

 adb logcat | grep 'SecretActivity' 

1898 1898 E SecretActivity: Try broadcast ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX

2. Tentamos lançar a transmissão com a ação do log:

 adb shell am broadcast -a ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX 

3. Um brinde aparecerá na tela com uma proposta para ver os logs pela tag SecretReceiver.

 adb logcat | grep 'SecretReceiver' 

1898 1898 E SecretReceiver: ISecretService.aidl
1898 1898 E SecretReceiver: package android.app;
1898 1898 E SecretReceiver: interface ISecretService {
1898 1898 E SecretReceiver: String getSecret();
1898 1898 E SecretReceiver: }
1898 1898 E SecretReceiver: See global settings to bind SecretService


4. Examinamos as configurações globais para encontrar uma ação para conectar-se ao SecretService.

 adb shell settings list global 

...
bind_secret_service_action=g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83
...


5. Estamos escrevendo um cliente para o serviço ISecretService usando essa interface de auxílio.

 public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(); intent.setAction("g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83"); PackageManager pm = getPackageManager(); List<ResolveInfo> resolveInfoList = pm.queryIntentServices(intent, 0); if (resolveInfoList == null || resolveInfoList.size() != 1) { return; } ResolveInfo serviceInfo = resolveInfoList.get(0); ComponentName component = new ComponentName(serviceInfo.serviceInfo.packageName, serviceInfo.serviceInfo.name); Intent explicitIntent = new Intent(intent); explicitIntent.setComponent(component); bindService(explicitIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ISecretService secretService = ISecretService.Stub.asInterface(service); try { Log.d("MainActivity", secretService.getSecret()); } catch (Exception e) { Log.e("MainActivity", "RemoteException", e); } } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE); } } 

6. Execute, nos logs, obtemos a resposta:

 adb logcat | grep 'MainActivity' 

1898 1898 E MainActivity: VENHz=qWr7y!t3ZhP!8Skw!!kcTkt7V%

hackear! # 6

Autor: Pavel Vorobkalov
Arquivo binário

Tupper escreveu um aplicativo para Android que envia mensagens.

Decifre a mensagem e siga em frente.

Formato da resposta: string como um link.

Solução

Instale e execute o aplicativo no dispositivo. Vemos dois botões: Enviar mensagem e Enviar fórmula. Quando você clica neles, nada visível acontece. Você precisa descobrir como e para onde o aplicativo envia algo.



Abrimos aplicativos .apk usando alguns meios de engenharia reversa e analisamos a Atividade principal. Lá você pode ver os métodos onSendMessageClick e onSendFormulaClick. Eles criam um Intent com a ação "com.yandex.tupper.action.NEW_MESSAGE" e um extra com a chave "com.yandex.tupper.message" e o enviam para os aplicativos da lista (usando o método geral). Eles obtêm a lista chamando PackageManager.queryBroadcastReceivers.

Você pode continuar usando as ferramentas de engenharia reversa para estudar a mensagem, extraindo-a do código do aplicativo. Mas esse caminho é intencionalmente (embora um pouco) complicado pela criptografia RSA adicional.

Existe outra maneira que melhor combina com a programação do Android. Você precisa escrever um aplicativo que lide com essa transmissão de intenção. Criamos nosso aplicativo, no receptor de registro de manifesto:

  <receiver android:name=".TupperMessageReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.yandex.tupper.action.NEW_MESSAGE" /> </intent-filter> </receiver> 

No manipulador, obtemos a mensagem extra e executamos nossa própria atividade, na qual mostraremos o resultado do processamento:

 public class TupperMessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); if (message == null) { return; } Intent activityIntent = new Intent(context, MainActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activityIntent.putExtra(MainActivity.EXTRA_MESSAGE, message); context.startActivity(activityIntent); } } 

Agora vamos lidar com a descriptografia da própria mensagem. Se você procurar no Yandex a mensagem de Tupper, poderá encontrar um artigo sobre a fórmula de Wupper. Ele tem um exemplo de uma fórmula codificada na forma de um número:

48584 ...
4858450636189713423582095962494202044581400587983244549483093085061934704708809928450644769865524364849997247024915119110411605739177407856919754326571855442057210445735883681829823754139634338225199452191651284348332905131193199953502413758765239264874613394906870130562295813219481113685339535565290850023875092856892694555974281546386510730049106723058933586052544096664351265349363643957125565695936815184334857605266940161251266951421550539554519153785457525756590740540157929001765967965480064427829131488548259914721248506352686630476300

Esse número é enviado como uma mensagem clicando no botão Enviar fórmula. O resultado da decodificação do número também está no artigo da Wikipedia. Ele pode ser usado para depurar sua implementação do algoritmo de decodificação.

Implementamos o algoritmo de decodificação em Java e, por uma questão de beleza, convertemos o número binário resultante em um Bitmap monocromático:

  public class TupperCodec { static final int TUPPER_R = 17; private TupperCodec() {} public static BigInteger decodeFromDecString(String decString) throws NumberFormatException { BigInteger data = new BigInteger(decString, 10); return data.divide(BigInteger.valueOf(TUPPER_R)); } public static String getNumberAsBinImage(BigInteger number) { String binString = number.toString(2); StringBuffer result = new StringBuffer(binString.length()); int i; for (i = 0; i < binString.length(); i++) { if ((i % TUPPER_R) == 0 && i > 0) { result.append('\n'); } result.append(binString.charAt(binString.length() - 1 - i)); } int numberOfRows = (binString.length() + TUPPER_R - 1) / TUPPER_R; for (; i < numberOfRows * TUPPER_R; i++) { result.append(~_~quot&#0;quot~_~); } return result.toString(); } public static Bitmap getBinImageAsBitmap(String binImage) { String[] lines = binImage.split("\n"); int[] colors = new int[lines.length * TUPPER_R]; for (int i = 0; i < lines.length; i++) { String line = lines[i]; assert line.length() == TUPPER_R; for (int j = 0; j < line.length(); j++) { colors[i * TUPPER_R + j] = line.charAt(j) == '0' ? Color.WHITE : Color.BLACK; } } return Bitmap.createBitmap(colors, TUPPER_R, lines.length, Bitmap.Config.ARGB_8888); } } 

Em Atividade, chame a decodificação e exiba o resultado como um Bitmap no ImageView:

  public class MainActivity extends AppCompatActivity { static final String EXTRA_MESSAGE = "com.yandex.tupper.message"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); if (intent != null && intent.hasExtra(MainActivity.EXTRA_MESSAGE)) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); try { BigInteger data = TupperCodec.decodeFromDecString(message); String binImage = TupperCodec.getNumberAsBinImage(data); Bitmap bitmap = TupperCodec.getBinImageAsBitmap(binImage); ImageView imageView = findViewById(R.id.message_image); imageView.setImageBitmap(bitmap); CharSequence toastText = TextUtils.ellipsize(message, new TextPaint(), 400, TextUtils.TruncateAt.END); Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show(); } catch (NumberFormatException ex) { Toast.makeText(this, getString(R.string.invalid_message), Toast.LENGTH_LONG).show(); } } } } 




clck.ru/FevR5 .

hack it! #7

:


.

. , , .

: - .



.apk zip- classes.dex — Java- . , , dex2jar Android Studio. dex2jar Java-, Android Studio class-. , .

, proguard ( android.support.* androidx.*, ), — com.yandex.contest.keygenme.MainActivity, Activity . , : MainActivity.a#doInBackground bbaab b , .



c d. N- π ( --), . , π hexed [c[i] + key[i]] = d[i], i = 0..11. , . 50 hex- π. : 537306089144. key_2056BE33E064.

, , — . doInBackground:



, , if (var12[0] == var8) {… }. var7 — i- . , var7 0 9 . . , .apk ( JVM) .

hack it! #8

:


N , . : Android 4.4, 1200 × 600, FM-. , , FM- . FM- , .

, FM-. .

, :

1. 106.00
2. (seek)
3. ,

, .

: . .

: , a(106.00) 106.00 , b(true) , c() , a(106.00);b(true);c()



: , FM- . .apk dex-. Java. , , — jadx.

.apk, . MainActivity. : fragment_holder RadioFullFragment . . RadioFullPresenter, ChangeRadioFrequencyUsecase, RadioRepo. .

«implements RadioRepo». : RadioRepoImpl. , b. ServiceConnection — . , b lambda$new$0$RadioRepoImpl. , $$Lambda$RadioRepoImpl$FYTR9gAgZEGvrLjkbkpAIup7OKw, serviceConnection IRadioService radioService. , RadioService.

: onServiceConnected «b radioManager = Stub.asInterface(service)». , . «bindService». bindService «com.some_company.help_service». , com.some_company.carradio.FmUtils. , , . , : j , : 8880 88.00 . 8800. a. , .

. : k z. RadioRepoImpl, enableRadio() disableRadio() k. z(boolean isForwardDirection), « (seek) ».

: a(10600);z(true);j().

hack it! #9

:


flatbuffers . GPX . . , .

FlatBuffers :

 namespace ru.yandex.android.task; struct GeoPoint { latitude:double; longitude:double; } table Points { items:[GeoPoint]; } root_type Points; 

: , sha256. .



, FlatBuffers.

1. fbs- .
2. Java-. JVM- , Gradle flatbuffers.
3. fbs-.
4. fbs-, - — . , FlatBuffers.
5. , . structs , struct table. .
6. Kotlin. FlatBuffers.

fbs , . FlatBufferBuilder input. input :

 val pointsList = Points.getRootAsPoints(buffer) val restoredPoints = (0 until pointsList.itemsLength()) .mapNotNull(pointsList::items) 

c , (lat, long). as-is GPX-.

GPX- ( ) . GPX XML. , XML- . XML- GPX-track-. XML- MAP- . «yandroid».

, . CLI-:

 echo "yandroid" | shasum -a 256 

.

Android


dig it! #1

:


QA- .

:

— ( 'Screen 1')
— GO NEXT ( 'Screen 2')
— GO BACK
— GO NEXT,
— 5–10

: 'Screen 2'.

: , « ».

.apk .



C stack trace, .

Exemplo

stack trace …

 at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:326) at android.os.Looper.loop(Looper.java:160) at com.android.server.SystemServer.run(SystemServer.java:454) at com.android.server.SystemServer.main(SystemServer.java:294) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838) 

… , RuntimeInit.java, 493, :

 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 



, , « ».

/data/anr/traces.txt /data/anr/anr_* (https://developer.android.com/topic/performance/vitals/anr#pull_a_traces_file). adb-:

 adb root adb shell ls /data/anr adb pull /data/anr/<filename> 

( adb root , Google API.)

stack trace . — stack trace, :

 com.droid.mission.anrapk.MainActivity$2.onClick(MainActivity.java:43) 



dig it! #2

:

Android Studio, , MultiDex. -, apk- .dex- (65536). / apk Andorid Studio .dex-.

— dex- , dex- ( , ) . dex- .

: multidex , android gradle plugin 3.0.0 – 3.1.4

.

?



, dex-. Gradle — , , .apk.

Java- dex- ART/Dalvik: transformClassesWithDexBuilderForDebug. dex-, . Android Gradle plugin (AGP) Gradle . , — , Gradle . , . AGP, — DSL- Gradle build.gradle.

3.1.* minimal-main-dex. , .

dig it! #3

:
— - .

?

: — .



, . Android WindowManager. . , WindowManager.LayoutParams . , , TYPE_APPLICATION .

, , . , , - .

, Android SDK, , . , WindowManager AOSP. , , , .

, , TYPE_BOOT_PROGRESS . getWindowLayerFromTypeLw WindowManagerPolicy. Javadoc :

Returns the layer assignment for the window type. Allows you to control how different kinds of windows are ordered on-screen

, , . switch-case , layer — TYPE_POINTER, 2018. .

dig it! #4

:

, - , .

. , , , .

Android , ?



, , Android SDK, .

startActivity Context . Context Activity — , startActivity . startActivityForResult. , Activity mInstrumentation.execStartActivity .

? startActivity ActivityManagerService, ServiceManager.getService(Context.ACTIVITY_SERVICE).

ActivityManagerService . startActivityAsUser, startActivity, ActivityStarter execute. , .

C startActivity . , abort , true START_ABORTED Activity.

, , mService.mIntentFirewall.checkStartActivity. , . :

This is called from ActivityManager to check if a start activity intent should be allowed.

, IntentFirewall . RULES_DIR . : ifw.

dig it! #5

:

. , (, ) — 8800. , , (+8800). , 8800 +8800 , / , .

. . , +8800 8800 .

, (java/c/c++). , .

: source.android.com
— : androidxref.com



Dialer , , URI authorities = "content://com.android.contacts" .

ContentProvider. URI ContactsPoviider. : , SQL- . :

 sb.append("PHONE_NUMBERS_EQUAL(" + Tables.DATA + "." + Phone.NUMBER + ", "); 

PHONE_NUMBERS_EQUAL — SQL-. sqlite .

sqlite, , . ContactsProvider , config_use_strict_phone_number_comparation, . :

 <bool name="config_use_strict_phone_number_comparation">true</bool> 

Source: https://habr.com/ru/post/pt484876/


All Articles