Merge "Add new "shell command" feature to Binder objects."
This commit is contained in:
committed by
Android (Google) Code Review
commit
d019d4ab3f
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
192
core/java/android/os/ShellCommand.java
Normal file
192
core/java/android/os/ShellCommand.java
Normal 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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user