1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-01 01:41:01 -08:00
emacs/test/infra/android/bin/AtsStub.java
Po Lu 08077788db ; Begin integrating facilities for executing ERT tests on Android
* test/infra/android/README:

* test/infra/android/bin/AtsStub.java (AtsStub):

* test/infra/android/bin/README:

* test/infra/android/test-controller.el (tramp)
(ats-adb-executable, ats-adb-host, ats-adb-infile, ats-cache)
(ats-adb-disable-stderr, ats-adb-device-regexp, ats-adb)
(ats-adb-process-filter, ats-start-adb, ats-enumerate-devices)
(ats-online-devices, ats-memoize, ats-ps-device, ats-getprop)
(ats-get-sdk-version, ats-package-list-regexp)
(ats-is-package-debuggable, ats-list-users, ats-get-package-aid)
(ats-aid-user-offset, ats-aid-isolated-start, ats-aid-app-start)
(ats-aid-to-uid, ats-uid-to-username, ats-verify-directory)
(ats-get-package-data-directory)
(ats-get-user-external-storage-directory, ats-transfer-padding)
(ats-exec-script, ats-exec-script-checked)
(ats-use-private-staging-directory, ats-get-staging-directory)
(ats-base64-available, ats-echo-n-e, ats-echo-c, ats-octab, c)
(ats-upload-encode-binary, ats-upload, ats-download)
(ats-create-empty-temporary, ats-run-jar)
(ats-supports-am-force-stop, ats-supports-am-force-stop-user)
(ats-kill-process-by-username-and-name)
(ats-portforward-local-type-regexp)
(ats-portforward-remote-type-regexp, ats-portforward-list-regexp)
(ats-portreverse-type-regexp, ats-portreverse-list-regexp)
(ats-reverse-list, ats-reverse-tcp, ats-forward-list)
(ats-forward-tcp, ats-is-tail-available, ats-java-int-min)
(ats-java-int-max, ats-java-long-min, ats-java-long-max)
(ats-intent-array-type, ats-fmt-array-element, ats-build-intent)
(ats-working-stub-file, ats-file-directory, ats-am-start-intent)
(ats-create-commfile, ats-watch-commfile, ats-server)
(ats-default-port, ats-accepting-connection)
(ats-address-to-hostname, ats-is-localhost-p)
(ats-server-sentinel, ats-server-log, ats-server-exists-p)
(ats-start-server, ats-await-connection-timeout)
(ats-await-connection, ats-forward-server-sentinel)
(ats-forward-server-filter, ats-reverse-server)
(ats-forward-server, ats-cancel-forward-server, ats-remote-port)
(ats-in-connection-context, ats-outstanding-reverse-connection)
(ats-terminate-reverse-safely, ats-disconnect-internal)
(ats-read-connection, ats-disconnect, ats-establish-connection)
(ats-connect, ats-eval, test-controller):

* test/infra/android/test-driver.el (ats-process)
(ats-connection-established, ats-header, ats-in-eval)
(ats-eval-as-printed, ats-eval-serial, ats-process-filter)
(ats-display-status-buffer, ats-establish-connection)
(ats-driver-log, ats-initiate-connection, test-driver): New
files.
2025-02-25 09:15:35 +08:00

332 lines
9.2 KiB
Java

/* Launch an intent stated on the command line as an activity. -*- c-file-style: "GNU" -*-
Copyright (C) 2025 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
package ats;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.net.Uri;
import java.lang.IllegalArgumentException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class AtsStub
{
public static final String IDENT = "$Id: AtsStub.java,v 1.4 2024/06/30 04:24:39 jw Exp $";
private static void
neutralizeApplicationThread (ActivityThread thread)
{
Field field;
try
{
field = ActivityThread.class.getDeclaredField ("mAppThread");
field.setAccessible (true);
field.set (thread, null);
}
catch (NoSuchFieldException x)
{
x.printStackTrace ();
}
catch (IllegalAccessException x)
{
x.printStackTrace ();
}
}
private static int
main1 (String[] argv)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException
{
ActivityThread thread;
Context context;
Looper.prepare ();
thread = ActivityThread.systemMain ();
context = thread.getSystemContext ();
if (argv.length < 1 || argv[0].equals ("--help"))
{
System.out.println ("AtsStub [start] [--user <USER_ID>] <INTENT>");
System.out.println (" where INTENT is a series of arguments defining an Intent,");
System.out.println (" namely,");
System.out.println (" -a <ACTION>");
System.out.println (" -d <DATA URI>");
System.out.println (" -t <TYPE>");
System.out.println (" -c <CATEGORY>");
System.out.println (" -n <COMPONENT>");
System.out.println (" -e or --es <KEY> <STRING VALUE>");
System.out.println (" --esn <KEY>");
System.out.println (" --ei <KEY> <INTEGER VALUE>");
System.out.println (" --eu <KEY> <URI VALUE>");
System.out.println (" --ecn <KEY> <COMPONENT NAME>");
System.out.println (" --eia <KEY> <INTEGER>, ...");
System.out.println (" --el <KEY> <LONG>");
System.out.println (" --ela <KEY> <LONG>, ...");
System.out.println (" --ef <KEY> <FLOAT>");
System.out.println (" --efa <KEY> <FLOAT ARRAY>");
System.out.println (" --esa <KEY> <STRING>, ...");
System.out.println (" --ez <KEY> <BOOLEAN>");
System.out.println (" -f <KEY> <FLAGS>");
return 0;
}
else if (argv[0].equals ("start"))
{
Intent intent;
int i, userID = 0;
String token, type;
Uri data;
boolean debug;
intent = new Intent ();
debug = false;
data = null;
type = null;
for (i = 1; i < argv.length; ++i)
{
int j;
token = argv[i];
if (token.equals ("-a"))
intent.setAction (argv[++i]);
else if (token.equals ("-d"))
data = Uri.parse (argv[++i]);
else if (token.equals ("-t"))
type = argv[++i];
else if (token.equals ("-c"))
intent.addCategory (argv[++i]);
else if (token.equals ("-e") || token.equals ("--es"))
{
intent.putExtra (argv[i + 1], argv[i + 2]);
i += 2;
}
else if (token.equals ("--esn"))
intent.putExtra (argv[++i], (String) null);
else if (token.equals ("--ei"))
{
int value = Integer.valueOf (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("--eu"))
{
Uri value = Uri.parse (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("--ecn"))
{
ComponentName value
= ComponentName.unflattenFromString (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("--eia"))
{
String values[] = argv[i + 2].split (",");
int array[] = new int[values.length];
for (j = 0; j < values.length; ++j)
array[j] = Integer.valueOf (values[j]);
intent.putExtra (argv[i + 1], array);
i += 2;
}
else if (token.equals ("--el"))
{
long value = Long.valueOf (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("--ela"))
{
String values[] = argv[i + 2].split (",");
long array[] = new long[values.length];
for (j = 0; j < values.length; ++j)
array[j] = Long.valueOf (values[j]);
intent.putExtra (argv[i + 1], array);
i += 2;
}
else if (token.equals ("--ef"))
{
float value = Float.valueOf (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("--efa"))
{
String values[] = argv[i + 2].split (",");
float array[] = new float[values.length];
for (j = 0; j < values.length; ++j)
array[j] = Float.valueOf (values[j]);
intent.putExtra (argv[i + 1], array);
i += 2;
}
else if (token.equals ("--esa"))
{
String[] strings;
strings = argv[i + 2].split ("(?<!\\\\),");
intent.putExtra (argv[i + 1], strings);
i += 2;
}
else if (token.equals ("--ez"))
{
boolean value = Boolean.valueOf (argv[i + 2]);
intent.putExtra (argv[i + 1], value);
i += 2;
}
else if (token.equals ("-n"))
{
ComponentName value
= ComponentName.unflattenFromString (argv[++i]);
if (value == null)
throw new IllegalArgumentException ("Invalid component name: " + argv[i]);
intent.setComponent (value);
}
else if (token.equals ("-f"))
intent.addFlags (Integer.decode (argv[++i]).intValue ());
else if (token.equals ("--user"))
{
int value = Integer.valueOf (argv[++i]);
if (value != 0
&& (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1))
throw new IllegalArgumentException ("Invalid user: " + value);
userID = value;
}
else
throw new IllegalArgumentException ("Option not understood: " + argv[i]);
}
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType (data, type);
if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
|| userID == 0)
{
/* mAppThread must be neutralized, or the ActivityManager
service will attempt and fail to locate a matching app
record when it is passed as the caller argument to the
startActivity RPC routine. */
neutralizeApplicationThread (thread);
context.startActivity (intent);
}
else
{
/* Otherwise, there are two revisions of startActivityAsUser
this utility must support, whose signatures follow:
(IApplicationThread, Intent, String, IBinder, String,
int, int, String, ParcelFileDescriptor, Bundle, int)
(IApplicationThread, String, Intent, String, IBinder, String,
int, int, String, ParcelFileDescriptor, Bundle, int) */
Method method;
IActivityManager am = ActivityManagerNative.getDefault ();
int rc;
Class klass = IActivityManager.class;
/* Attempt to resolve the first variant which is mostly
observed on Jelly Bean MR1 systems. */
try
{
method = klass.getMethod ("startActivityAsUser",
IApplicationThread.class,
Intent.class, String.class,
IBinder.class, String.class,
int.class, int.class,
String.class,
ParcelFileDescriptor.class,
Bundle.class, int.class);
}
catch (NoSuchMethodException e)
{
method = null;
}
if (method != null)
rc = (Integer) method.invoke (am, null, intent, intent.getType (),
null, null, 0, 0, null, null, null,
userID);
else
{
/* Now the modern `IActivityManager#startActivityAsUser'. */
method = klass.getMethod ("startActivityAsUser",
IApplicationThread.class,
String.class, Intent.class,
String.class, IBinder.class,
String.class, int.class,
int.class, String.class,
ParcelFileDescriptor.class,
Bundle.class, int.class);
rc = (Integer) method.invoke (am, null, null, intent,
intent.getType (),
null, null, 0, 0, null,
null, null, userID);
}
if (rc != 0)
{
System.err.println ("Failed to start activity as user: " + rc);
return 1;
}
}
return 0;
}
return 1;
}
public static void
main (String arg[])
{
try
{
System.exit (main1 (arg));
}
catch (Throwable e)
{
e.printStackTrace ();
System.exit (0);
}
}
};