Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8bd64df2 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Help for the debugging help for issue #8734824.

Add a new "hang" am command that lets you hang the system
process.  Useful for testing.

Change-Id: Ice0fc52b49d80e5189f016108b03f9fd549b58a7
parent 5b88a2fd
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -94,6 +95,7 @@ public class Am extends BaseCommand {
                "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
                "       am clear-debug-app\n" +
                "       am monitor [--gdb <port>]\n" +
                "       am hang [--allow-restart]\n" +
                "       am screen-compat [on|off] <PACKAGE>\n" +
                "       am to-uri [INTENT]\n" +
                "       am to-intent-uri [INTENT]\n" +
@@ -169,6 +171,9 @@ public class Am extends BaseCommand {
                "am monitor: start monitoring for crashes or ANRs.\n" +
                "    --gdb: start gdbserv on the given port at crash/ANR\n" +
                "\n" +
                "am hang: hang the system.\n" +
                "    --allow-restart: allow watchdog to perform normal system restart\n" +
                "\n" +
                "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
                "\n" +
                "am to-uri: print the given Intent specification as a URI.\n" +
@@ -249,6 +254,8 @@ public class Am extends BaseCommand {
            runBugReport();
        } else if (op.equals("monitor")) {
            runMonitor();
        } else if (op.equals("hang")) {
            runHang();
        } else if (op.equals("screen-compat")) {
            runScreenCompat();
        } else if (op.equals("to-uri")) {
@@ -1304,6 +1311,22 @@ public class Am extends BaseCommand {
        controller.run();
    }

    private void runHang() throws Exception {
        String opt;
        boolean allowRestart = false;
        while ((opt=nextOption()) != null) {
            if (opt.equals("--allow-restart")) {
                allowRestart = true;
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }

        System.out.println("Hanging the system...");
        mAm.hang(new Binder(), allowRestart);
    }

    private void runScreenCompat() throws Exception {
        String mode = nextArgRequired();
        boolean enabled;
+21 −0
Original line number Diff line number Diff line
@@ -1870,6 +1870,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
        }

        case HANG_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder who = data.readStrongBinder();
            boolean allowRestart = data.readInt() != 0;
            hang(who, allowRestart);
            reply.writeNoException();
            return true;
        }

        }

        return super.onTransact(code, data, reply, flags);
@@ -4270,5 +4279,17 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
    }

    public void hang(IBinder who, boolean allowRestart) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(who);
        data.writeInt(allowRestart ? 1 : 0);
        mRemote.transact(HANG_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

    private IBinder mRemote;
}
+3 −0
Original line number Diff line number Diff line
@@ -377,6 +377,8 @@ public interface IActivityManager extends IInterface {

    public void killUid(int uid, String reason) throws RemoteException;

    public void hang(IBinder who, boolean allowRestart) throws RemoteException;

    /*
     * Private non-Binder interfaces
     */
@@ -638,4 +640,5 @@ public interface IActivityManager extends IInterface {
    int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163;
    int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
    int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
    int HANG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166;
}
+14 −3
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ public class Watchdog extends Thread {

    int mPhonePid;
    IActivityController mController;
    boolean mAllowRestart = true;

    final Calendar mCalendar = Calendar.getInstance();
    int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
@@ -233,6 +234,12 @@ public class Watchdog extends Thread {
        }
    }

    public void setAllowRestart(boolean allowRestart) {
        synchronized (this) {
            mAllowRestart = allowRestart;
        }
    }

    public void addMonitor(Monitor monitor) {
        synchronized (this) {
            if (isAlive()) {
@@ -401,6 +408,7 @@ public class Watchdog extends Thread {


            final String name;
            final boolean allowRestart;
            synchronized (this) {
                long timeout = TIME_TO_WAIT;

@@ -437,6 +445,7 @@ public class Watchdog extends Thread {

                name = (mCurrentMonitor != null) ?
                    mCurrentMonitor.getClass().getName() : "null";
                allowRestart = mAllowRestart;
            }

            // If we got here, that means that the system is most likely hung.
@@ -506,12 +515,14 @@ public class Watchdog extends Thread {
            }

            // Only kill the process if the debugger is not attached.
            if (!Debug.isDebuggerConnected()) {
            if (Debug.isDebuggerConnected()) {
                Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
            } else if (!allowRestart) {
                Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
            } else {
                Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
                Process.killProcess(Process.myPid());
                System.exit(10);
            } else {
                Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
            }

            waitedHalf = false;
+39 −0
Original line number Diff line number Diff line
@@ -7813,6 +7813,45 @@ public final class ActivityManagerService extends ActivityManagerNative
        return killed;
    }
    @Override
    public void hang(final IBinder who, boolean allowRestart) {
        if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.SET_ACTIVITY_WATCHER);
        }
        final IBinder.DeathRecipient death = new DeathRecipient() {
            @Override
            public void binderDied() {
                synchronized (this) {
                    notifyAll();
                }
            }
        };
        try {
            who.linkToDeath(death, 0);
        } catch (RemoteException e) {
            Slog.w(TAG, "hang: given caller IBinder is already dead.");
            return;
        }
        synchronized (this) {
            Watchdog.getInstance().setAllowRestart(allowRestart);
            Slog.i(TAG, "Hanging system process at request of pid " + Binder.getCallingPid());
            synchronized (death) {
                while (who.isBinderAlive()) {
                    try {
                        death.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            Watchdog.getInstance().setAllowRestart(true);
        }
    }
    public final void startRunning(String pkg, String cls, String action,
            String data) {
        synchronized(this) {