diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/ActivityManager.java | 57 | ||||
-rw-r--r-- | core/java/android/os/BatteryStats.java | 6 | ||||
-rw-r--r-- | core/java/com/android/internal/os/BatteryStatsImpl.java | 4 | ||||
-rw-r--r-- | core/java/com/android/internal/os/TransferPipe.java | 242 |
4 files changed, 303 insertions, 6 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 63ac42e..f523b55 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,8 +16,10 @@ package android.app; +import android.os.IBinder; import com.android.internal.app.IUsageStats; import com.android.internal.os.PkgUsageStats; +import com.android.internal.os.TransferPipe; import com.android.internal.util.MemInfoReader; import android.content.ComponentName; @@ -49,6 +51,9 @@ import android.util.Log; import android.util.Slog; import android.view.Display; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -1709,7 +1714,7 @@ public class ActivityManager { public ComponentName importanceReasonComponent; /** - * When {@link importanceReasonPid} is non-0, this is the importance + * When {@link #importanceReasonPid} is non-0, this is the importance * of the other pid. @hide */ public int importanceReasonImportance; @@ -2181,4 +2186,54 @@ public class ActivityManager { return false; } } + + /** + * Perform a system dump of various state associated with the given application + * package name. This call blocks while the dump is being performed, so should + * not be done on a UI thread. The data will be written to the given file + * descriptor as text. An application must hold the + * {@link android.Manifest.permission#DUMP} permission to make this call. + * @param fd The file descriptor that the dump should be written to. + * @param packageName The name of the package that is to be dumped. + */ + public void dumpPackageState(FileDescriptor fd, String packageName) { + dumpPackageStateStatic(fd, packageName); + } + + /** + * @hide + */ + public static void dumpPackageStateStatic(FileDescriptor fd, String packageName) { + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new PrintWriter(fout); + dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName}); + pw.println(); + dumpService(pw, fd, "package", new String[] { packageName}); + pw.println(); + dumpService(pw, fd, "batteryinfo", new String[] { packageName}); + pw.flush(); + } + + private static void dumpService(PrintWriter pw, FileDescriptor fd, String name, String[] args) { + pw.print("DUMP OF SERVICE "); pw.print(name); pw.println(":"); + IBinder service = ServiceManager.checkService(name); + if (service == null) { + pw.println(" (Service not found)"); + return; + } + TransferPipe tp = null; + try { + pw.flush(); + tp = new TransferPipe(); + tp.setBufferPrefix(" "); + service.dump(tp.getWriteFd().getFileDescriptor(), args); + tp.go(fd); + } catch (Throwable e) { + if (tp != null) { + tp.kill(); + } + pw.println("Failure dumping service:"); + e.printStackTrace(pw); + } + } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 32bbfbd..fef1818 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2279,7 +2279,7 @@ public abstract class BatteryStats implements Parcelable { * @param pw a Printer to receive the dump output. */ @SuppressWarnings("unused") - public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly) { + public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly, int reqUid) { prepareForDumpLocked(); long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); @@ -2336,11 +2336,11 @@ public abstract class BatteryStats implements Parcelable { pw.println("Statistics since last charge:"); pw.println(" System starts: " + getStartCount() + ", currently on battery: " + getIsOnBattery()); - dumpLocked(pw, "", STATS_SINCE_CHARGED, -1); + dumpLocked(pw, "", STATS_SINCE_CHARGED, reqUid); pw.println(""); } pw.println("Statistics since last unplugged:"); - dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, -1); + dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, reqUid); } @SuppressWarnings("unused") diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 38a8c19..949a499 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6038,7 +6038,7 @@ public final class BatteryStatsImpl extends BatteryStats { updateNetworkActivityLocked(); } - public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly) { + public void dumpLocked(PrintWriter pw, boolean isUnpluggedOnly, int reqUid) { if (DEBUG) { Printer pr = new PrintWriterPrinter(pw); pr.println("*** Screen timer:"); @@ -6068,6 +6068,6 @@ public final class BatteryStatsImpl extends BatteryStats { pr.println("*** Bluetooth timer:"); mBluetoothOnTimer.logState(pr, " "); } - super.dumpLocked(pw, isUnpluggedOnly); + super.dumpLocked(pw, isUnpluggedOnly, reqUid); } } diff --git a/core/java/com/android/internal/os/TransferPipe.java b/core/java/com/android/internal/os/TransferPipe.java new file mode 100644 index 0000000..068d914 --- /dev/null +++ b/core/java/com/android/internal/os/TransferPipe.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011 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.internal.os; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Slog; + +/** + * Helper for transferring data through a pipe from a client app. + */ +public final class TransferPipe implements Runnable { + static final String TAG = "TransferPipe"; + static final boolean DEBUG = false; + + static final long DEFAULT_TIMEOUT = 5000; // 5 seconds + + final Thread mThread;; + final ParcelFileDescriptor[] mFds; + + FileDescriptor mOutFd; + long mEndTime; + String mFailure; + boolean mComplete; + + String mBufferPrefix; + + interface Caller { + void go(IInterface iface, FileDescriptor fd, String prefix, + String[] args) throws RemoteException; + } + + public TransferPipe() throws IOException { + mThread = new Thread(this, "TransferPipe"); + mFds = ParcelFileDescriptor.createPipe(); + } + + ParcelFileDescriptor getReadFd() { + return mFds[0]; + } + + public ParcelFileDescriptor getWriteFd() { + return mFds[1]; + } + + public void setBufferPrefix(String prefix) { + mBufferPrefix = prefix; + } + + static void go(Caller caller, IInterface iface, FileDescriptor out, + String prefix, String[] args) throws IOException, RemoteException { + go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT); + } + + static void go(Caller caller, IInterface iface, FileDescriptor out, + String prefix, String[] args, long timeout) throws IOException, RemoteException { + if ((iface.asBinder()) instanceof Binder) { + // This is a local object... just call it directly. + try { + caller.go(iface, out, prefix, args); + } catch (RemoteException e) { + } + return; + } + + TransferPipe tp = new TransferPipe(); + try { + caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args); + tp.go(out, timeout); + } finally { + tp.kill(); + } + } + + static void goDump(IBinder binder, FileDescriptor out, + String[] args) throws IOException, RemoteException { + goDump(binder, out, args, DEFAULT_TIMEOUT); + } + + static void goDump(IBinder binder, FileDescriptor out, + String[] args, long timeout) throws IOException, RemoteException { + if (binder instanceof Binder) { + // This is a local object... just call it directly. + try { + binder.dump(out, args); + } catch (RemoteException e) { + } + return; + } + + TransferPipe tp = new TransferPipe(); + try { + binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args); + tp.go(out, timeout); + } finally { + tp.kill(); + } + } + + public void go(FileDescriptor out) throws IOException { + go(out, DEFAULT_TIMEOUT); + } + + public void go(FileDescriptor out, long timeout) throws IOException { + try { + synchronized (this) { + mOutFd = out; + mEndTime = SystemClock.uptimeMillis() + timeout; + + if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd() + + " out=" + out); + + // Close the write fd, so we know when the other side is done. + closeFd(1); + + mThread.start(); + + while (mFailure == null && !mComplete) { + long waitTime = mEndTime - SystemClock.uptimeMillis(); + if (waitTime <= 0) { + if (DEBUG) Slog.i(TAG, "TIMEOUT!"); + mThread.interrupt(); + throw new IOException("Timeout"); + } + + try { + wait(waitTime); + } catch (InterruptedException e) { + } + } + + if (DEBUG) Slog.i(TAG, "Finished: " + mFailure); + if (mFailure != null) { + throw new IOException(mFailure); + } + } + } finally { + kill(); + } + } + + void closeFd(int num) { + if (mFds[num] != null) { + if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]); + try { + mFds[num].close(); + } catch (IOException e) { + } + mFds[num] = null; + } + } + + public void kill() { + closeFd(0); + closeFd(1); + } + + @Override + public void run() { + final byte[] buffer = new byte[1024]; + final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor()); + final FileOutputStream fos = new FileOutputStream(mOutFd); + + if (DEBUG) Slog.i(TAG, "Ready to read pipe..."); + byte[] bufferPrefix = null; + boolean needPrefix = true; + if (mBufferPrefix != null) { + bufferPrefix = mBufferPrefix.getBytes(); + } + + int size; + try { + while ((size=fis.read(buffer)) > 0) { + if (DEBUG) Slog.i(TAG, "Got " + size + " bytes"); + if (bufferPrefix == null) { + fos.write(buffer, 0, size); + } else { + int start = 0; + for (int i=0; i<size; i++) { + if (buffer[i] != '\n') { + if (i > start) { + fos.write(buffer, start, i-start); + } + start = i; + if (needPrefix) { + fos.write(bufferPrefix); + needPrefix = false; + } + do { + i++; + } while (i<size && buffer[i] != '\n'); + if (i < size) { + needPrefix = true; + } + } + } + if (size > start) { + fos.write(buffer, start, size-start); + } + } + } + if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size); + if (mThread.isInterrupted()) { + if (DEBUG) Slog.i(TAG, "Interrupted!"); + } + } catch (IOException e) { + synchronized (this) { + mFailure = e.toString(); + notifyAll(); + return; + } + } + + synchronized (this) { + mComplete = true; + notifyAll(); + } + } +} |