am 700ab1c7: Merge "DO NOT MERGE. Integrate from master: Improve activity manager debug dumps." into honeycomb-mr2

* commit '700ab1c75862babba90f54e02bdc877becd4f4fc':
  DO NOT MERGE.  Integrate from master: Improve activity manager debug dumps.
This commit is contained in:
Dianne Hackborn
2011-05-16 11:06:28 -07:00
committed by Android Git Automerger
15 changed files with 961 additions and 336 deletions

View File

@ -143329,6 +143329,21 @@
<parameter name="args" type="java.lang.String[]">
</parameter>
</method>
<method name="dumpAsync"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="fd" type="java.io.FileDescriptor">
</parameter>
<parameter name="args" type="java.lang.String[]">
</parameter>
</method>
<method name="flushPendingCommands"
return="void"
abstract="false"
@ -147801,6 +147816,23 @@
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="dumpAsync"
return="void"
abstract="true"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="fd" type="java.io.FileDescriptor">
</parameter>
<parameter name="args" type="java.lang.String[]">
</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
<method name="getInterfaceDescriptor"
return="java.lang.String"
abstract="true"
@ -150155,6 +150187,21 @@
visibility="public"
>
</method>
<method name="dup"
return="android.os.ParcelFileDescriptor"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="orig" type="java.io.FileDescriptor">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="fromSocket"
return="android.os.ParcelFileDescriptor"
abstract="false"

View File

@ -186,11 +186,16 @@ static void dumpstate() {
run_command("DUMPSYS", 60, "dumpsys", NULL);
printf("========================================================\n");
printf("== Application Services\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
/* Instead of a 60s timeout, we should give each service a 5 second timeout */
run_command("APP SERVICES", 60, "dumpsys", "activity", "service", NULL);
run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
}

View File

@ -367,11 +367,10 @@ public final class ActivityThread {
}
static final class DumpComponentInfo {
FileDescriptor fd;
ParcelFileDescriptor fd;
IBinder token;
String prefix;
String[] args;
boolean dumped;
}
static final class ResultData {
@ -639,20 +638,13 @@ public final class ActivityThread {
public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
data.fd = fd;
data.token = servicetoken;
data.args = args;
data.dumped = false;
queueOrSendMessage(H.DUMP_SERVICE, data);
synchronized (data) {
while (!data.dumped) {
try {
data.wait();
} catch (InterruptedException e) {
// no need to do anything here, we will keep waiting until
// dumped is set
}
}
try {
data.fd = ParcelFileDescriptor.dup(fd);
data.token = servicetoken;
data.args = args;
queueOrSendMessage(H.DUMP_SERVICE, data);
} catch (IOException e) {
Slog.w(TAG, "dumpService failed", e);
}
}
@ -714,21 +706,14 @@ public final class ActivityThread {
public void dumpActivity(FileDescriptor fd, IBinder activitytoken,
String prefix, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
data.fd = fd;
data.token = activitytoken;
data.prefix = prefix;
data.args = args;
data.dumped = false;
queueOrSendMessage(H.DUMP_ACTIVITY, data);
synchronized (data) {
while (!data.dumped) {
try {
data.wait();
} catch (InterruptedException e) {
// no need to do anything here, we will keep waiting until
// dumped is set
}
}
try {
data.fd = ParcelFileDescriptor.dup(fd);
data.token = activitytoken;
data.prefix = prefix;
data.args = args;
queueOrSendMessage(H.DUMP_ACTIVITY, data);
} catch (IOException e) {
Slog.w(TAG, "dumpActivity failed", e);
}
}
@ -2154,33 +2139,27 @@ public final class ActivityThread {
}
private void handleDumpService(DumpComponentInfo info) {
try {
Service s = mServices.get(info.token);
if (s != null) {
PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
s.dump(info.fd, pw, info.args);
pw.close();
}
} finally {
synchronized (info) {
info.dumped = true;
info.notifyAll();
Service s = mServices.get(info.token);
if (s != null) {
PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
s.dump(info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
try {
info.fd.close();
} catch (IOException e) {
}
}
}
private void handleDumpActivity(DumpComponentInfo info) {
try {
ActivityClientRecord r = mActivities.get(info.token);
if (r != null && r.activity != null) {
PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
r.activity.dump(info.prefix, info.fd, pw, info.args);
pw.close();
}
} finally {
synchronized (info) {
info.dumped = true;
info.notifyAll();
ActivityClientRecord r = mActivities.get(info.token);
if (r != null && r.activity != null) {
PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
pw.flush();
try {
info.fd.close();
} catch (IOException e) {
}
}
}

View File

@ -845,7 +845,7 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeFileDescriptor(fd);
data.writeStrongBinder(token);
data.writeStringArray(args);
mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, 0);
mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
@ -967,7 +967,7 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeStrongBinder(token);
data.writeString(prefix);
data.writeStringArray(args);
mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, 0);
mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}

View File

@ -303,45 +303,106 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("{ scale=");
sb.append("{ fnt=");
sb.append(fontScale);
sb.append(" imsi=");
sb.append(mcc);
sb.append("/");
sb.append(mnc);
sb.append(" loc=");
sb.append(locale);
sb.append(" touch=");
sb.append(touchscreen);
sb.append(" keys=");
sb.append(keyboard);
sb.append("/");
sb.append(keyboardHidden);
sb.append("/");
sb.append(hardKeyboardHidden);
sb.append(" nav=");
sb.append(navigation);
sb.append("/");
sb.append(navigationHidden);
sb.append(" orien=");
switch(orientation) {
case ORIENTATION_LANDSCAPE:
sb.append("L"); break;
case ORIENTATION_PORTRAIT:
sb.append("P"); break;
default:
sb.append(orientation);
if (locale != null) {
sb.append(" ");
sb.append(locale);
} else {
sb.append(" (no locale)");
}
switch (touchscreen) {
case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break;
case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break;
case TOUCHSCREEN_STYLUS: sb.append(" stylus"); break;
case TOUCHSCREEN_FINGER: sb.append(" finger"); break;
default: sb.append(" touch="); sb.append(touchscreen); break;
}
switch (keyboard) {
case KEYBOARD_UNDEFINED: sb.append(" ?keyb"); break;
case KEYBOARD_NOKEYS: sb.append(" -keyb"); break;
case KEYBOARD_QWERTY: sb.append(" qwerty"); break;
case KEYBOARD_12KEY: sb.append(" 12key"); break;
default: sb.append(" keys="); sb.append(keyboard); break;
}
switch (keyboardHidden) {
case KEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
case KEYBOARDHIDDEN_NO: sb.append("/v"); break;
case KEYBOARDHIDDEN_YES: sb.append("/h"); break;
case KEYBOARDHIDDEN_SOFT: sb.append("/s"); break;
default: sb.append("/"); sb.append(keyboardHidden); break;
}
switch (hardKeyboardHidden) {
case HARDKEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
case HARDKEYBOARDHIDDEN_NO: sb.append("/v"); break;
case HARDKEYBOARDHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(hardKeyboardHidden); break;
}
switch (navigation) {
case NAVIGATION_UNDEFINED: sb.append(" ?nav"); break;
case NAVIGATION_NONAV: sb.append(" -nav"); break;
case NAVIGATION_DPAD: sb.append(" dpad"); break;
case NAVIGATION_TRACKBALL: sb.append(" tball"); break;
case NAVIGATION_WHEEL: sb.append(" wheel"); break;
default: sb.append(" nav="); sb.append(navigation); break;
}
switch (navigationHidden) {
case NAVIGATIONHIDDEN_UNDEFINED: sb.append("/?"); break;
case NAVIGATIONHIDDEN_NO: sb.append("/v"); break;
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
switch (orientation) {
case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
case ORIENTATION_PORTRAIT: sb.append(" port"); break;
default: sb.append(" orien="); sb.append(orientation); break;
}
switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break;
case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break;
case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break;
case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break;
default: sb.append(" layoutSize=");
sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break;
}
switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) {
case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break;
case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break;
case SCREENLAYOUT_LONG_YES: sb.append(" long"); break;
default: sb.append(" layoutLong=");
sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
}
switch ((uiMode&UI_MODE_TYPE_MASK)) {
case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break;
case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
case UI_MODE_TYPE_CAR: sb.append(" car"); break;
default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
}
switch ((uiMode&UI_MODE_NIGHT_MASK)) {
case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break;
case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break;
case UI_MODE_NIGHT_YES: sb.append(" night"); break;
default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break;
}
if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
} else {
sb.append("?wdp");
}
if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
} else {
sb.append("?hdp");
}
sb.append(" layout=0x");
sb.append(java.lang.Integer.toHexString(screenLayout));
sb.append(" uiMode=0x");
sb.append(java.lang.Integer.toHexString(uiMode));
sb.append(" wdp=");
sb.append(screenWidthDp);
sb.append(" hdp=");
sb.append(screenHeightDp);
if (seq != 0) {
sb.append(" seq=");
sb.append(" s.");
sb.append(seq);
}
sb.append('}');

View File

@ -255,6 +255,25 @@ public class Binder implements IBinder {
}
}
/**
* Like {@link #dump(FileDescriptor, String[])}, but ensures the target
* executes asynchronously.
*/
public void dumpAsync(final FileDescriptor fd, final String[] args) {
final FileOutputStream fout = new FileOutputStream(fd);
final PrintWriter pw = new PrintWriter(fout);
Thread thr = new Thread("Binder.dumpAsync") {
public void run() {
try {
dump(fd, pw, args);
} finally {
pw.flush();
}
}
};
thr.start();
}
/**
* Print the object's state into the given stream.
*
@ -364,6 +383,20 @@ final class BinderProxy implements IBinder {
}
}
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeFileDescriptor(fd);
data.writeStringArray(args);
try {
transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
reply.readException();
} finally {
data.recycle();
reply.recycle();
}
}
BinderProxy() {
mSelf = new WeakReference(this);
}

View File

@ -156,6 +156,16 @@ public interface IBinder {
*/
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
* to perform the dump.
*
* @param fd The raw file descriptor that the dump is being sent to.
* @param args additional arguments to the dump request.
*/
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
/**
* Perform a generic operation with the object.
*

View File

@ -1383,6 +1383,8 @@ public final class Parcel {
private native FileDescriptor internalReadFileDescriptor();
/*package*/ static native FileDescriptor openFileDescriptor(String file,
int mode) throws FileNotFoundException;
/*package*/ static native FileDescriptor dupFileDescriptor(FileDescriptor orig)
throws IOException;
/*package*/ static native void closeFileDescriptor(FileDescriptor desc)
throws IOException;
/*package*/ static native void clearFileDescriptor(FileDescriptor desc);

View File

@ -116,6 +116,17 @@ public class ParcelFileDescriptor implements Parcelable {
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
/**
* Create a new ParcelFileDescriptor that is a dup of an existing
* FileDescriptor. This obeys standard POSIX semantics, where the
* new file descriptor shared state such as file position with the
* original file descriptor.
*/
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
FileDescriptor fd = Parcel.dupFileDescriptor(orig);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
/**
* Create a new ParcelFileDescriptor from the specified Socket.
*

View File

@ -1509,7 +1509,31 @@ static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
int fd = open(name8.string(), flags, realMode);
if (fd < 0) {
jniThrowException(env, "java/io/FileNotFoundException", NULL);
jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
return NULL;
}
jobject object = newFileDescriptor(env, fd);
if (object == NULL) {
close(fd);
}
return object;
}
static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig)
{
if (orig == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return NULL;
}
int origfd = env->GetIntField(orig, gFileDescriptorOffsets.mDescriptor);
if (origfd < 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
return NULL;
}
int fd = dup(origfd);
if (fd < 0) {
jniThrowException(env, "java/io/IOException", strerror(errno));
return NULL;
}
jobject object = newFileDescriptor(env, fd);
@ -1521,6 +1545,10 @@ static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
{
if (object == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return;
}
int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
if (fd >= 0) {
env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
@ -1531,6 +1559,10 @@ static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jo
static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
{
if (object == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return;
}
int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
if (fd >= 0) {
env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
@ -1735,6 +1767,7 @@ static const JNINativeMethod gParcelMethods[] = {
{"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
{"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
{"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
{"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor},
{"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},
{"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
{"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer},

View File

@ -136,7 +136,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
pw.print(" isHomeActivity="); pw.println(isHomeActivity);
pw.print(prefix); pw.print("configuration="); pw.println(configuration);
pw.print(prefix); pw.print("config="); pw.println(configuration);
if (resultTo != null || resultWho != null) {
pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
pw.print(" resultWho="); pw.print(resultWho);

View File

@ -60,7 +60,7 @@ class ContentProviderRecord extends ContentProviderHolder {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("package=");
pw.print(info.applicationInfo.packageName);
pw.print("process="); pw.println(info.processName);
pw.print(" process="); pw.println(info.processName);
pw.print(prefix); pw.print("app="); pw.println(app);
if (launchingApp != null) {
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);

View File

@ -211,7 +211,7 @@ class ServiceRecord extends Binder {
pw.print(" lastActivity=");
TimeUtils.formatDuration(lastActivity, now, pw);
pw.println("");
pw.print(prefix); pw.print(" executingStart=");
pw.print(prefix); pw.print("executingStart=");
TimeUtils.formatDuration(executingStart, now, pw);
pw.print(" restartTime=");
TimeUtils.formatDuration(restartTime, now, pw);

View File

@ -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.server.am;
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.
*/
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;
}
TransferPipe() throws IOException {
mThread = new Thread(this, "TransferPipe");
mFds = ParcelFileDescriptor.createPipe();
}
ParcelFileDescriptor getReadFd() {
return mFds[0];
}
ParcelFileDescriptor getWriteFd() {
return mFds[1];
}
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();
}
}
void go(FileDescriptor out) throws IOException {
go(out, DEFAULT_TIMEOUT);
}
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;
}
}
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();
}
}
}