diff options
-rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 23 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 21 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 3 | ||||
-rw-r--r-- | services/java/com/android/server/Watchdog.java | 17 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 39 |
5 files changed, 100 insertions, 3 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 93658e1..61fe340 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -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; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 98baa0e..d4478bf 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -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; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 33a2770..a21caee 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -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; } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index e784cf2..3aec4ea 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -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; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index cbeed7b..0081dfc 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -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) { |