Merge "Add new "shell command" feature to Binder objects."

This commit is contained in:
Dianne Hackborn
2015-10-12 21:41:39 +00:00
committed by Android (Google) Code Review
4 changed files with 536 additions and 205 deletions

View File

@@ -19,6 +19,7 @@ package android.os;
import android.util.Log;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -342,11 +343,7 @@ public class Binder implements IBinder {
try {
dump(fd.getFileDescriptor(), args);
} finally {
try {
fd.close();
} catch (IOException e) {
// swallowed, not propagated back to the caller
}
IoUtils.closeQuietly(fd);
}
}
// Write the StrictMode header.
@@ -356,6 +353,31 @@ public class Binder implements IBinder {
StrictMode.clearGatheredViolations();
}
return true;
} else if (code == SHELL_COMMAND_TRANSACTION) {
ParcelFileDescriptor in = data.readFileDescriptor();
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(err);
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
}
return true;
}
return false;
}
@@ -368,33 +390,37 @@ public class Binder implements IBinder {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new FastPrintWriter(fout);
try {
final String disabled;
synchronized (Binder.class) {
disabled = sDumpDisabled;
}
if (disabled == null) {
try {
dump(fd, pw, args);
} catch (SecurityException e) {
pw.println("Security exception: " + e.getMessage());
throw e;
} catch (Throwable e) {
// Unlike usual calls, in this case if an exception gets thrown
// back to us we want to print it back in to the dump data, since
// that is where the caller expects all interesting information to
// go.
pw.println();
pw.println("Exception occurred while dumping:");
e.printStackTrace(pw);
}
} else {
pw.println(sDumpDisabled);
}
doDump(fd, pw, args);
} finally {
pw.flush();
}
}
void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
final String disabled;
synchronized (Binder.class) {
disabled = sDumpDisabled;
}
if (disabled == null) {
try {
dump(fd, pw, args);
} catch (SecurityException e) {
pw.println("Security exception: " + e.getMessage());
throw e;
} catch (Throwable e) {
// Unlike usual calls, in this case if an exception gets thrown
// back to us we want to print it back in to the dump data, since
// that is where the caller expects all interesting information to
// go.
pw.println();
pw.println("Exception occurred while dumping:");
e.printStackTrace(pw);
}
} else {
pw.println(sDumpDisabled);
}
}
/**
* Like {@link #dump(FileDescriptor, String[])}, but ensures the target
* executes asynchronously.
@@ -425,6 +451,34 @@ public class Binder implements IBinder {
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
}
/**
* @param in The raw file descriptor that an input data stream can be read from.
* @param out The raw file descriptor that normal command messages should be written to.
* @param err The raw file descriptor that command error messages should be written to.
* @param args Command-line arguments.
* @param resultReceiver Called when the command has finished executing, with the result code.
* @throws RemoteException
* @hide
*/
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
onShellCommand(in, out, err, args, resultReceiver);
}
/**
* Handle a call to {@link #shellCommand}. The default implementation simply prints
* an error message. Override and replace with your own.
* @hide
*/
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
FileOutputStream fout = new FileOutputStream(err != null ? err : out);
PrintWriter pw = new FastPrintWriter(fout);
pw.println("No shell command implementation.");
pw.flush();
resultReceiver.send(0, null);
}
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
@@ -590,6 +644,24 @@ final class BinderProxy implements IBinder {
}
}
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeFileDescriptor(in);
data.writeFileDescriptor(out);
data.writeFileDescriptor(err);
data.writeStringArray(args);
resultReceiver.writeToParcel(data, 0);
try {
transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
reply.readException();
} finally {
data.recycle();
reply.recycle();
}
}
BinderProxy() {
mSelf = new WeakReference(this);
}

View File

@@ -102,6 +102,12 @@ public interface IBinder {
*/
int DUMP_TRANSACTION = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
/**
* IBinder protocol transaction code: execute a shell command.
* @hide
*/
int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';
/**
* IBinder protocol transaction code: interrogate the recipient side
* of the transaction for its canonical interface descriptor.
@@ -187,7 +193,7 @@ public interface IBinder {
* the transact() method.
*/
public IInterface queryLocalInterface(String descriptor);
/**
* Print the object's state into the given stream.
*
@@ -195,7 +201,7 @@ public interface IBinder {
* @param args additional arguments to the dump request.
*/
public void dump(FileDescriptor fd, String[] args) throws RemoteException;
/**
* Like {@link #dump(FileDescriptor, String[])} but always executes
* asynchronously. If the object is local, a new thread is created
@@ -206,6 +212,20 @@ public interface IBinder {
*/
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
/**
* Execute a shell command on this object. This may be performed asynchrously from the caller;
* the implementation must always call resultReceiver when finished.
*
* @param in The raw file descriptor that an input data stream can be read from.
* @param out The raw file descriptor that normal command messages should be written to.
* @param err The raw file descriptor that command error messages should be written to.
* @param args Command-line arguments.
* @param resultReceiver Called when the command has finished executing, with the result code.
* @hide
*/
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException;
/**
* Perform a generic operation with the object.
*

View File

@@ -0,0 +1,192 @@
/*
* 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 android.os;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
/**
* @hide
*/
public abstract class ShellCommand {
static final String TAG = "ShellCommand";
static final boolean DEBUG = false;
private Binder mTarget;
private FileDescriptor mIn;
private FileDescriptor mOut;
private FileDescriptor mErr;
private String[] mArgs;
private ResultReceiver mResultReceiver;
private String mCmd;
private int mArgPos;
private String mCurArgData;
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
public void exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
mTarget = target;
mIn = in;
mOut = out;
mErr = err;
mArgs = args;
mResultReceiver = resultReceiver;
mCmd = args != null && args.length > 0 ? args[0] : null;
mArgPos = 1;
mCurArgData = null;
mOutPrintWriter = null;
mErrPrintWriter = null;
if (DEBUG) Slog.d(TAG, "Starting command " + mCmd + " on " + mTarget);
int res = -1;
try {
res = onCommand(mCmd);
if (DEBUG) Slog.d(TAG, "Executed command " + mCmd + " on " + mTarget);
} catch (SecurityException e) {
PrintWriter eout = getErrPrintWriter();
eout.println("Security exception: " + e.getMessage());
eout.println();
e.printStackTrace(eout);
} catch (Throwable e) {
// Unlike usual calls, in this case if an exception gets thrown
// back to us we want to print it back in to the dump data, since
// that is where the caller expects all interesting information to
// go.
PrintWriter eout = getErrPrintWriter();
eout.println();
eout.println("Exception occurred while dumping:");
e.printStackTrace(eout);
} finally {
if (DEBUG) Slog.d(TAG, "Flushing output streams on " + mTarget);
if (mOutPrintWriter != null) {
mOutPrintWriter.flush();
}
if (mErrPrintWriter != null) {
mErrPrintWriter.flush();
}
if (DEBUG) Slog.d(TAG, "Sending command result on " + mTarget);
mResultReceiver.send(res, null);
}
if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
}
public PrintWriter getOutPrintWriter() {
if (mOutPrintWriter == null) {
FileOutputStream fout = new FileOutputStream(mOut);
mOutPrintWriter = new FastPrintWriter(fout);
}
return mOutPrintWriter;
}
public PrintWriter getErrPrintWriter() {
if (mErr == null) {
return getOutPrintWriter();
}
if (mErrPrintWriter == null) {
FileOutputStream fout = new FileOutputStream(mErr);
mErrPrintWriter = new FastPrintWriter(fout);
}
return mErrPrintWriter;
}
/**
* Return the next option on the command line -- that is an argument that
* starts with '-'. If the next argument is not an option, null is returned.
*/
public String getNextOption() {
if (mCurArgData != null) {
String prev = mArgs[mArgPos - 1];
throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
}
if (mArgPos >= mArgs.length) {
return null;
}
String arg = mArgs[mArgPos];
if (!arg.startsWith("-")) {
return null;
}
mArgPos++;
if (arg.equals("--")) {
return null;
}
if (arg.length() > 1 && arg.charAt(1) != '-') {
if (arg.length() > 2) {
mCurArgData = arg.substring(2);
return arg.substring(0, 2);
} else {
mCurArgData = null;
return arg;
}
}
mCurArgData = null;
return arg;
}
/**
* Return the next argument on the command line, whatever it is; if there are
* no arguments left, return null.
*/
public String getNextArg() {
if (mCurArgData != null) {
String arg = mCurArgData;
mCurArgData = null;
return arg;
} else if (mArgPos < mArgs.length) {
return mArgs[mArgPos++];
} else {
return null;
}
}
/**
* Return the next argument on the command line, whatever it is; if there are
* no arguments left, throws an IllegalArgumentException to report this to the user.
*/
public String getNextArgRequired() {
String arg = getNextArg();
if (arg == null) {
String prev = mArgs[mArgPos - 1];
throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
}
return arg;
}
public int handleDefaultCommands(String cmd) {
if ("dump".equals(cmd)) {
String[] newArgs = new String[mArgs.length-1];
System.arraycopy(mArgs, 1, newArgs, 0, mArgs.length-1);
mTarget.doDump(mOut, getOutPrintWriter(), newArgs);
return 0;
} else if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
onHelp();
} else {
getOutPrintWriter().println("Unknown command: " + cmd);
}
return -1;
}
public abstract int onCommand(String cmd);
public abstract void onHelp();
}

View File

@@ -55,7 +55,9 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -743,6 +745,8 @@ public class DeviceIdleController extends SystemService
final MyHandler mHandler;
BinderService mBinderService;
private final class BinderService extends IDeviceIdleController.Stub {
@Override public void addPowerSaveWhitelistApp(String name) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
@@ -794,37 +798,20 @@ public class DeviceIdleController extends SystemService
@Override public void addPowerSaveTempWhitelistApp(String packageName, long duration,
int userId, String reason) throws RemoteException {
getContext().enforceCallingPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
userId = ActivityManagerNative.getDefault().handleIncomingUser(
Binder.getCallingPid(),
callingUid,
userId,
/*allowAll=*/ false,
/*requireFull=*/ false,
"addPowerSaveTempWhitelistApp", null);
final long token = Binder.clearCallingIdentity();
try {
DeviceIdleController.this.addPowerSaveTempWhitelistAppInternal(callingUid,
packageName, duration, userId, true, reason);
} finally {
Binder.restoreCallingIdentity(token);
}
addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
}
@Override public long addPowerSaveTempWhitelistAppForMms(String packageName,
int userId, String reason) throws RemoteException {
long duration = mConstants.MMS_TEMP_APP_WHITELIST_DURATION;
addPowerSaveTempWhitelistApp(packageName, duration, userId, reason);
addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
return duration;
}
@Override public long addPowerSaveTempWhitelistAppForSms(String packageName,
int userId, String reason) throws RemoteException {
long duration = mConstants.SMS_TEMP_APP_WHITELIST_DURATION;
addPowerSaveTempWhitelistApp(packageName, duration, userId, reason);
addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
return duration;
}
@@ -837,6 +824,11 @@ public class DeviceIdleController extends SystemService
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DeviceIdleController.this.dump(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);
}
}
public final class LocalService {
@@ -912,7 +904,8 @@ public class DeviceIdleController extends SystemService
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
}
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, new BinderService());
mBinderService = new BinderService();
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
publishLocalService(LocalService.class, new LocalService());
}
@@ -1104,11 +1097,33 @@ public class DeviceIdleController extends SystemService
}
}
void addPowerSaveTempWhitelistAppChecked(String packageName, long duration,
int userId, String reason) throws RemoteException {
getContext().enforceCallingPermission(
Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
"No permission to change device idle whitelist");
final int callingUid = Binder.getCallingUid();
userId = ActivityManagerNative.getDefault().handleIncomingUser(
Binder.getCallingPid(),
callingUid,
userId,
/*allowAll=*/ false,
/*requireFull=*/ false,
"addPowerSaveTempWhitelistApp", null);
final long token = Binder.clearCallingIdentity();
try {
addPowerSaveTempWhitelistAppInternal(callingUid,
packageName, duration, userId, true, reason);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
* Adds an app to the temporary whitelist and resets the endTime for granting the
* app an exemption to access network and acquire wakelocks.
*/
public void addPowerSaveTempWhitelistAppInternal(int callingUid, String packageName,
void addPowerSaveTempWhitelistAppInternal(int callingUid, String packageName,
long duration, int userId, boolean sync, String reason) {
try {
int uid = getContext().getPackageManager().getPackageUid(packageName, userId);
@@ -1122,7 +1137,7 @@ public class DeviceIdleController extends SystemService
* Adds an app to the temporary whitelist and resets the endTime for granting the
* app an exemption to access network and acquire wakelocks.
*/
public void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int appId,
void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int appId,
long duration, boolean sync, String reason) {
final long timeNow = SystemClock.elapsedRealtime();
Runnable networkPolicyTempWhitelistCallback = null;
@@ -1698,11 +1713,10 @@ public class DeviceIdleController extends SystemService
out.endDocument();
}
private void dumpHelp(PrintWriter pw) {
pw.println("Device idle controller (deviceidle) dump options:");
pw.println(" [-h] [CMD]");
pw.println(" -h: print this help text.");
pw.println("Commands:");
static void dumpHelp(PrintWriter pw) {
pw.println("Device idle controller (deviceidle) commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" step");
pw.println(" Immediately step to next state, without waiting for alarm.");
pw.println(" force-idle");
@@ -1718,10 +1732,184 @@ public class DeviceIdleController extends SystemService
pw.println(" Print currently whitelisted apps.");
pw.println(" whitelist [package ...]");
pw.println(" Add (prefix with +) or remove (prefix with -) packages.");
pw.println(" tempwhitelist [package ..]");
pw.println(" tempwhitelist [-u] [package ..]");
pw.println(" Temporarily place packages in whitelist for 10 seconds.");
}
class Shell extends ShellCommand {
int userId = UserHandle.USER_SYSTEM;
@Override
public int onCommand(String cmd) {
return onShellCommand(this, cmd);
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
dumpHelp(pw);
}
}
int onShellCommand(Shell shell, String cmd) {
PrintWriter pw = shell.getOutPrintWriter();
if ("step".equals(cmd)) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
stepIdleStateLocked();
pw.print("Stepped to: "); pw.println(stateToString(mState));
} finally {
Binder.restoreCallingIdentity(token);
}
}
} else if ("force-idle".equals(cmd)) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
if (!mEnabled) {
pw.println("Unable to go idle; not enabled");
return -1;
}
mForceIdle = true;
becomeInactiveIfAppropriateLocked();
int curState = mState;
while (curState != STATE_IDLE) {
stepIdleStateLocked();
if (curState == mState) {
pw.print("Unable to go idle; stopped at ");
pw.println(stateToString(mState));
exitForceIdleLocked();
return -1;
}
curState = mState;
}
pw.println("Now forced in to idle mode");
} finally {
Binder.restoreCallingIdentity(token);
}
}
} else if ("disable".equals(cmd)) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
if (mEnabled) {
mEnabled = false;
becomeActiveLocked("disabled", Process.myUid());
pw.println("Idle mode disabled");
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
} else if ("enable".equals(cmd)) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
if (!mEnabled) {
mEnabled = true;
becomeInactiveIfAppropriateLocked();
pw.println("Idle mode enabled");
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
} else if ("enabled".equals(cmd)) {
synchronized (this) {
pw.println(mEnabled ? "1" : " 0");
}
} else if ("whitelist".equals(cmd)) {
long token = Binder.clearCallingIdentity();
try {
String arg = shell.getNextArg();
if (arg != null) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
do {
if (arg.length() < 1 || (arg.charAt(0) != '-'
&& arg.charAt(0) != '+')) {
pw.println("Package must be prefixed with + or -: " + arg);
return -1;
}
char op = arg.charAt(0);
String pkg = arg.substring(1);
if (op == '+') {
if (addPowerSaveWhitelistAppInternal(pkg)) {
pw.println("Added: " + pkg);
} else {
pw.println("Unknown package: " + pkg);
}
} else {
if (removePowerSaveWhitelistAppInternal(pkg)) {
pw.println("Removed: " + pkg);
}
}
} while ((arg=shell.getNextArg()) != null);
} else {
synchronized (this) {
for (int j=0; j<mPowerSaveWhitelistAppsExceptIdle.size(); j++) {
pw.print("system-excidle,");
pw.print(mPowerSaveWhitelistAppsExceptIdle.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistAppsExceptIdle.valueAt(j));
}
for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
pw.print("system,");
pw.print(mPowerSaveWhitelistApps.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistApps.valueAt(j));
}
for (int j=0; j<mPowerSaveWhitelistUserApps.size(); j++) {
pw.print("user,");
pw.print(mPowerSaveWhitelistUserApps.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistUserApps.valueAt(j));
}
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
} else if ("tempwhitelist".equals(cmd)) {
String opt;
while ((opt=shell.getNextOption()) != null) {
if ("-u".equals(opt)) {
opt = shell.getNextArg();
if (opt == null) {
pw.println("-u requires a user number");
return -1;
}
shell.userId = Integer.parseInt(opt);
}
}
String arg = shell.getNextArg();
if (arg != null) {
try {
addPowerSaveTempWhitelistAppChecked(arg, 10000L, shell.userId, "shell");
} catch (RemoteException re) {
pw.println("Failed: " + re);
}
} else {
pw.println("At least one package name must be specified");
return -1;
}
} else {
return shell.handleDefaultCommands(cmd);
}
return 0;
}
void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1746,156 +1934,15 @@ public class DeviceIdleController extends SystemService
}
} else if ("-a".equals(arg)) {
// Ignore, we always dump all.
} else if ("step".equals(arg)) {
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
stepIdleStateLocked();
pw.print("Stepped to: "); pw.println(stateToString(mState));
} finally {
Binder.restoreCallingIdentity(token);
}
}
return;
} else if ("force-idle".equals(arg)) {
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
if (!mEnabled) {
pw.println("Unable to go idle; not enabled");
return;
}
mForceIdle = true;
becomeInactiveIfAppropriateLocked();
int curState = mState;
while (curState != STATE_IDLE) {
stepIdleStateLocked();
if (curState == mState) {
pw.print("Unable to go idle; stopped at ");
pw.println(stateToString(mState));
exitForceIdleLocked();
return;
}
curState = mState;
}
pw.println("Now forced in to idle mode");
} finally {
Binder.restoreCallingIdentity(token);
}
}
return;
} else if ("disable".equals(arg)) {
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
if (mEnabled) {
mEnabled = false;
becomeActiveLocked("disabled", Process.myUid());
pw.println("Idle mode disabled");
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
return;
} else if ("enable".equals(arg)) {
synchronized (this) {
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
if (!mEnabled) {
mEnabled = true;
becomeInactiveIfAppropriateLocked();
pw.println("Idle mode enabled");
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
return;
} else if ("enabled".equals(arg)) {
synchronized (this) {
pw.println(mEnabled ? "1" : " 0");
}
return;
} else if ("whitelist".equals(arg)) {
long token = Binder.clearCallingIdentity();
try {
i++;
if (i < args.length) {
while (i < args.length) {
arg = args[i];
i++;
if (arg.length() < 1 || (arg.charAt(0) != '-'
&& arg.charAt(0) != '+')) {
pw.println("Package must be prefixed with + or -: " + arg);
return;
}
char op = arg.charAt(0);
String pkg = arg.substring(1);
if (op == '+') {
if (addPowerSaveWhitelistAppInternal(pkg)) {
pw.println("Added: " + pkg);
} else {
pw.println("Unknown package: " + pkg);
}
} else {
if (removePowerSaveWhitelistAppInternal(pkg)) {
pw.println("Removed: " + pkg);
}
}
}
} else {
synchronized (this) {
for (int j=0; j<mPowerSaveWhitelistAppsExceptIdle.size(); j++) {
pw.print("system-excidle,");
pw.print(mPowerSaveWhitelistAppsExceptIdle.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistAppsExceptIdle.valueAt(j));
}
for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
pw.print("system,");
pw.print(mPowerSaveWhitelistApps.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistApps.valueAt(j));
}
for (int j=0; j<mPowerSaveWhitelistUserApps.size(); j++) {
pw.print("user,");
pw.print(mPowerSaveWhitelistUserApps.keyAt(j));
pw.print(",");
pw.println(mPowerSaveWhitelistUserApps.valueAt(j));
}
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
return;
} else if ("tempwhitelist".equals(arg)) {
long token = Binder.clearCallingIdentity();
try {
i++;
if (i >= args.length) {
pw.println("At least one package name must be specified");
return;
}
while (i < args.length) {
arg = args[i];
i++;
addPowerSaveTempWhitelistAppInternal(0, arg, 10000L, userId, true,
"shell");
pw.println("Added: " + arg);
}
} finally {
Binder.restoreCallingIdentity(token);
}
return;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
return;
} else {
pw.println("Unknown command: " + arg);
Shell shell = new Shell();
shell.userId = userId;
String[] newArgs = new String[args.length-i];
System.arraycopy(args, i, newArgs, 0, args.length-i);
shell.exec(mBinderService, null, fd, null, newArgs, new ResultReceiver(null));
return;
}
}