diff options
4 files changed, 377 insertions, 124 deletions
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index d64273a36eac..73c2c804bdf1 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -44,7 +44,7 @@ public abstract class ShellCommand { private FastPrintWriter mOutPrintWriter; private FastPrintWriter mErrPrintWriter; - public void exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, + public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver) { mTarget = target; mIn = in; @@ -89,6 +89,7 @@ public abstract class ShellCommand { mResultReceiver.send(res, null); } if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget); + return res; } public PrintWriter getOutPrintWriter() { diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 2eeaec96b581..61fe62fb88a4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -19,6 +19,8 @@ package com.android.server; import android.database.ContentObserver; import android.os.BatteryStats; +import android.os.ResultReceiver; +import android.os.ShellCommand; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; import com.android.server.lights.Light; @@ -96,7 +98,6 @@ public final class BatteryService extends SystemService { // discharge stats before the device dies. private int mCriticalBatteryLevel; - private static final int DUMP_MAX_LENGTH = 24 * 1024; private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" }; private static final String DUMPSYS_DATA_PATH = "/data/system/"; @@ -106,6 +107,7 @@ public final class BatteryService extends SystemService { private final Context mContext; private final IBatteryStats mBatteryStats; + BinderService mBinderService; private final Handler mHandler; private final Object mLock = new Object(); @@ -162,7 +164,18 @@ public final class BatteryService extends SystemService { // watch for invalid charger messages if the invalid_charger switch exists if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) { - mInvalidChargerObserver.startObserving( + UEventObserver invalidChargerObserver = new UEventObserver() { + @Override + public void onUEvent(UEvent event) { + final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0; + synchronized (mLock) { + if (mInvalidCharger != invalidCharger) { + mInvalidCharger = invalidCharger; + } + } + } + }; + invalidChargerObserver.startObserving( "DEVPATH=/devices/virtual/switch/invalid_charger"); } } @@ -178,7 +191,8 @@ public final class BatteryService extends SystemService { // Should never happen. } - publishBinderService("battery", new BinderService()); + mBinderService = new BinderService(); + publishBinderService("battery", mBinderService); publishLocalService(BatteryManagerInternal.class, new LocalService()); } @@ -593,7 +607,6 @@ public final class BatteryService extends SystemService { } catch (NumberFormatException e) { Slog.e(TAG, "Invalid DischargeThresholds GService string: " + durationThresholdString + " or " + dischargeThresholdString); - return; } } } @@ -616,27 +629,40 @@ public final class BatteryService extends SystemService { } } - private void dumpInternal(PrintWriter pw, String[] args) { - synchronized (mLock) { - if (args == null || args.length == 0 || "-a".equals(args[0])) { - pw.println("Current Battery Service state:"); - if (mUpdatesStopped) { - pw.println(" (UPDATES STOPPED -- use 'reset' to restart)"); - } - pw.println(" AC powered: " + mBatteryProps.chargerAcOnline); - pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline); - pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline); - pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent); - pw.println(" status: " + mBatteryProps.batteryStatus); - pw.println(" health: " + mBatteryProps.batteryHealth); - pw.println(" present: " + mBatteryProps.batteryPresent); - pw.println(" level: " + mBatteryProps.batteryLevel); - pw.println(" scale: " + BATTERY_SCALE); - pw.println(" voltage: " + mBatteryProps.batteryVoltage); - pw.println(" temperature: " + mBatteryProps.batteryTemperature); - pw.println(" technology: " + mBatteryProps.batteryTechnology); + class Shell extends ShellCommand { + @Override + public int onCommand(String cmd) { + return onShellCommand(this, cmd); + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + dumpHelp(pw); + } + } + + static void dumpHelp(PrintWriter pw) { + pw.println("Battery service (battery) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" set [ac|usb|wireless|status|level|invalid] <value>"); + pw.println(" Force a battery property value, freezing battery state."); + pw.println(" unplug"); + pw.println(" Force battery unplugged, freezing battery state."); + pw.println(" reset"); + pw.println(" Unfreeze battery state, returning to current hardware values."); + } - } else if ("unplug".equals(args[0])) { + int onShellCommand(Shell shell, String cmd) { + if (cmd == null) { + return shell.handleDefaultCommands(cmd); + } + PrintWriter pw = shell.getOutPrintWriter(); + switch (cmd) { + case "unplug": { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); if (!mUpdatesStopped) { mLastBatteryProps.set(mBatteryProps); } @@ -650,30 +676,50 @@ public final class BatteryService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } + } break; + case "set": { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + final String key = shell.getNextArg(); + if (key == null) { + pw.println("No property specified"); + return -1; - } else if (args.length == 3 && "set".equals(args[0])) { - String key = args[1]; - String value = args[2]; + } + final String value = shell.getNextArg(); + if (value == null) { + pw.println("No value specified"); + return -1; + + } try { if (!mUpdatesStopped) { mLastBatteryProps.set(mBatteryProps); } boolean update = true; - if ("ac".equals(key)) { - mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0; - } else if ("usb".equals(key)) { - mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0; - } else if ("wireless".equals(key)) { - mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0; - } else if ("status".equals(key)) { - mBatteryProps.batteryStatus = Integer.parseInt(value); - } else if ("level".equals(key)) { - mBatteryProps.batteryLevel = Integer.parseInt(value); - } else if ("invalid".equals(key)) { - mInvalidCharger = Integer.parseInt(value); - } else { - pw.println("Unknown set option: " + key); - update = false; + switch (key) { + case "ac": + mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0; + break; + case "usb": + mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0; + break; + case "wireless": + mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0; + break; + case "status": + mBatteryProps.batteryStatus = Integer.parseInt(value); + break; + case "level": + mBatteryProps.batteryLevel = Integer.parseInt(value); + break; + case "invalid": + mInvalidCharger = Integer.parseInt(value); + break; + default: + pw.println("Unknown set option: " + key); + update = false; + break; } if (update) { long ident = Binder.clearCallingIdentity(); @@ -686,9 +732,12 @@ public final class BatteryService extends SystemService { } } catch (NumberFormatException ex) { pw.println("Bad value: " + value); + return -1; } - - } else if (args.length == 1 && "reset".equals(args[0])) { + } break; + case "reset": { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); try { if (mUpdatesStopped) { @@ -699,26 +748,38 @@ public final class BatteryService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } - } else { - pw.println("Dump current battery state, or:"); - pw.println(" set [ac|usb|wireless|status|level|invalid] <value>"); - pw.println(" unplug"); - pw.println(" reset"); - } + } break; + default: + return shell.handleDefaultCommands(cmd); } + return 0; } - private final UEventObserver mInvalidChargerObserver = new UEventObserver() { - @Override - public void onUEvent(UEventObserver.UEvent event) { - final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0; - synchronized (mLock) { - if (mInvalidCharger != invalidCharger) { - mInvalidCharger = invalidCharger; + private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mLock) { + if (args == null || args.length == 0 || "-a".equals(args[0])) { + pw.println("Current Battery Service state:"); + if (mUpdatesStopped) { + pw.println(" (UPDATES STOPPED -- use 'reset' to restart)"); } + pw.println(" AC powered: " + mBatteryProps.chargerAcOnline); + pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline); + pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline); + pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent); + pw.println(" status: " + mBatteryProps.batteryStatus); + pw.println(" health: " + mBatteryProps.batteryHealth); + pw.println(" present: " + mBatteryProps.batteryPresent); + pw.println(" level: " + mBatteryProps.batteryLevel); + pw.println(" scale: " + BATTERY_SCALE); + pw.println(" voltage: " + mBatteryProps.batteryVoltage); + pw.println(" temperature: " + mBatteryProps.batteryTemperature); + pw.println(" technology: " + mBatteryProps.batteryTechnology); + } else { + Shell shell = new Shell(); + shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null)); } } - }; + } private final class Led { private final Light mBatteryLight; @@ -776,8 +837,7 @@ public final class BatteryService extends SystemService { } private final class BatteryListener extends IBatteryPropertiesListener.Stub { - @Override - public void batteryPropertiesChanged(BatteryProperties props) { + @Override public void batteryPropertiesChanged(BatteryProperties props) { final long identity = Binder.clearCallingIdentity(); try { BatteryService.this.update(props); @@ -788,8 +848,7 @@ public final class BatteryService extends SystemService { } private final class BinderService extends Binder { - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -799,7 +858,12 @@ public final class BatteryService extends SystemService { return; } - dumpInternal(pw, args); + dumpInternal(fd, pw, args); + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ResultReceiver resultReceiver) { + (new Shell()).exec(this, in, out, err, args, resultReceiver); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index dfe5751267fd..d5ee5e846277 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -69,6 +69,7 @@ import android.graphics.Rect; import android.os.BatteryStats; import android.os.PersistableBundle; import android.os.PowerManager; +import android.os.ResultReceiver; import android.os.Trace; import android.os.TransactionTooLargeException; import android.os.WorkSource; @@ -13083,6 +13084,13 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ResultReceiver resultReceiver) { + (new ActivityManagerShellCommand(this, false)).exec( + this, in, out, err, args, resultReceiver); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (checkCallingPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -13119,34 +13127,7 @@ public final class ActivityManagerService extends ActivityManagerNative } dumpClient = true; } else if ("-h".equals(opt)) { - pw.println("Activity manager dump options:"); - pw.println(" [-a] [-c] [-p package] [-h] [cmd] ..."); - pw.println(" cmd may be one of:"); - pw.println(" a[ctivities]: activity stack state"); - pw.println(" r[recents]: recent activities state"); - pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state"); - pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state"); - pw.println(" p[rocesses] [PACKAGE_NAME]: process state"); - pw.println(" o[om]: out of memory management"); - pw.println(" perm[issions]: URI permission grant state"); - pw.println(" prov[iders] [COMP_SPEC ...]: content provider state"); - pw.println(" provider [COMP_SPEC]: provider client-side state"); - pw.println(" s[ervices] [COMP_SPEC ...]: service state"); - pw.println(" as[sociations]: tracked app associations"); - pw.println(" service [COMP_SPEC]: service client-side state"); - pw.println(" package [PACKAGE_NAME]: all state related to given package"); - pw.println(" all: dump all activities"); - pw.println(" top: dump the top activity"); - pw.println(" write: write all pending state to storage"); - pw.println(" track-associations: enable association tracking"); - pw.println(" untrack-associations: disable and clear association tracking"); - pw.println(" cmd may also be a COMP_SPEC to dump activities."); - pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),"); - pw.println(" a partial substring in a component name, a"); - pw.println(" hex object identifier."); - pw.println(" -a: include all available server state."); - pw.println(" -c: include client state."); - pw.println(" -p: limit output to given package."); + ActivityManagerShellCommand.dumpHelp(pw, true); return; } else { pw.println("Unknown argument: " + opt + "; use -h for help"); @@ -13283,36 +13264,15 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage); } - } else if ("write".equals(cmd)) { - mTaskPersister.flush(); - pw.println("All tasks persisted."); - return; - } else if ("track-associations".equals(cmd)) { - synchronized (this) { - if (!mTrackingAssociations) { - mTrackingAssociations = true; - pw.println("Association tracking started."); - } else { - pw.println("Association tracking already enabled."); - } - } - return; - } else if ("untrack-associations".equals(cmd)) { - synchronized (this) { - if (mTrackingAssociations) { - mTrackingAssociations = false; - mAssociations.clear(); - pw.println("Association tracking stopped."); - } else { - pw.println("Association tracking not running."); - } - } - return; } else { // Dumping a single activity? if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) { - pw.println("Bad activity command, or no activities match: " + cmd); - pw.println("Use -h for help."); + ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true); + int res = shell.exec(this, null, fd, null, args, new ResultReceiver(null)); + if (res < 0) { + pw.println("Bad activity command, or no activities match: " + cmd); + pw.println("Use -h for help."); + } } } if (!more) { @@ -13563,17 +13523,34 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mActiveUids.size() > 0) { - if (needSep) { - pw.println(); + boolean printed = false; + int whichAppId = -1; + if (dumpPackage != null) { + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( + dumpPackage, 0); + whichAppId = UserHandle.getAppId(info.uid); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } } - pw.println(" UID states:"); for (int i=0; i<mActiveUids.size(); i++) { UidRecord uidRec = mActiveUids.valueAt(i); + if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { + continue; + } + if (!printed) { + printed = true; + if (needSep) { + pw.println(); + } + pw.println(" UID states:"); + needSep = true; + printedAnything = true; + } pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid); pw.print(": "); pw.println(uidRec); } - needSep = true; - printedAnything = true; } if (mLruProcesses.size() > 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java new file mode 100644 index 000000000000..d1e7e85b6d51 --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.app.IActivityManager; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.os.UserHandle; + +import java.io.PrintWriter; + +class ActivityManagerShellCommand extends ShellCommand { + // IPC interface to activity manager -- don't need to do additional security checks. + final IActivityManager mInterface; + + // Internal service impl -- must perform security checks before touching. + final ActivityManagerService mInternal; + + final boolean mDumping; + + ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) { + mInterface = service; + mInternal = service; + mDumping = dumping; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "force-stop": + return runForceStop(pw); + case "kill": + return runKill(pw); + case "kill-all": + return runKillAll(pw); + case "write": + return runWrite(pw); + case "track-associations": + return runTrackAssociations(pw); + case "untrack-associations": + return runUntrackAssociations(pw); + default: + return handleDefaultCommands(cmd); + } + } catch (RemoteException e) { + pw.println("Remote exception: " + e); + } + return -1; + } + + int runForceStop(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = parseUserArg(getNextArgRequired()); + } else { + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + mInterface.forceStopPackage(getNextArgRequired(), userId); + return 0; + } + + int runKill(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt=getNextOption()) != null) { + if (opt.equals("--user")) { + userId = parseUserArg(getNextArgRequired()); + } else { + pw.println("Error: Unknown option: " + opt); + return -1; + } + } + mInterface.killBackgroundProcesses(getNextArgRequired(), userId); + return 0; + } + + int runKillAll(PrintWriter pw) throws RemoteException { + mInterface.killAllBackgroundProcesses(); + return 0; + } + + int runWrite(PrintWriter pw) { + mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, + "registerUidObserver()"); + mInternal.mTaskPersister.flush(); + pw.println("All tasks persisted."); + return 0; + } + + int runTrackAssociations(PrintWriter pw) { + mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, + "registerUidObserver()"); + synchronized (mInternal) { + if (!mInternal.mTrackingAssociations) { + mInternal.mTrackingAssociations = true; + pw.println("Association tracking started."); + } else { + pw.println("Association tracking already enabled."); + } + } + return 0; + } + + int runUntrackAssociations(PrintWriter pw) { + mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, + "registerUidObserver()"); + synchronized (mInternal) { + if (mInternal.mTrackingAssociations) { + mInternal.mTrackingAssociations = false; + mInternal.mAssociations.clear(); + pw.println("Association tracking stopped."); + } else { + pw.println("Association tracking not running."); + } + } + return 0; + } + + int parseUserArg(String arg) { + int userId; + if ("all".equals(arg)) { + userId = UserHandle.USER_ALL; + } else if ("current".equals(arg) || "cur".equals(arg)) { + userId = UserHandle.USER_CURRENT; + } else { + try { + userId = Integer.parseInt(arg); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Bad user number: " + arg); + } + } + return userId; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + dumpHelp(pw, mDumping); + } + + static void dumpHelp(PrintWriter pw, boolean dumping) { + if (dumping) { + pw.println("Activity manager dump options:"); + pw.println(" [-a] [-c] [-p PACKAGE] [-h] [WHAT] ..."); + pw.println(" WHAT may be one of:"); + pw.println(" a[ctivities]: activity stack state"); + pw.println(" r[recents]: recent activities state"); + pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state"); + pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state"); + pw.println(" p[rocesses] [PACKAGE_NAME]: process state"); + pw.println(" o[om]: out of memory management"); + pw.println(" perm[issions]: URI permission grant state"); + pw.println(" prov[iders] [COMP_SPEC ...]: content provider state"); + pw.println(" provider [COMP_SPEC]: provider client-side state"); + pw.println(" s[ervices] [COMP_SPEC ...]: service state"); + pw.println(" as[sociations]: tracked app associations"); + pw.println(" service [COMP_SPEC]: service client-side state"); + pw.println(" package [PACKAGE_NAME]: all state related to given package"); + pw.println(" all: dump all activities"); + pw.println(" top: dump the top activity"); + pw.println(" WHAT may also be a COMP_SPEC to dump activities."); + pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),"); + pw.println(" a partial substring in a component name, a"); + pw.println(" hex object identifier."); + pw.println(" -a: include all available server state."); + pw.println(" -c: include client state."); + pw.println(" -p: limit output to given package."); + } else { + pw.println("Activity manager (activity) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>"); + pw.println(" Complete stop the given application package."); + pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>"); + pw.println(" Kill all processes associated with the given application."); + pw.println(" kill-all"); + pw.println(" Kill all processes that are safe to kill (cached, etc)"); + pw.println(" write"); + pw.println(" Write all pending state to storage."); + pw.println(" track-associations"); + pw.println(" Enable association tracking."); + pw.println(" untrack-associations"); + pw.println(" Disable and clear association tracking."); + } + } +} |