Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e3eb8e5 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #27408705: Runtime restart: WATCHDOG KILLING SYSTEM PROCESS...

...Blocked in monitor com.android.s

There was a change awhile ago to make the IInstrumentationWatcher
callbacks synchronous, to avoid issues with them spamming non-responsive
watches and filling the binder transfer buffer.  However, you can't
just do this, because the activity manager calls these with its
lock held.

To allow them to stay synchronous with the activity manager getting
blocked on the watcher, introduce a new thread for dispatching calls
to the watcher.  This thread is created as needed, and dispatches
a queue of callback commands to make to instrumentation watchers.
The callback is still synchronous, so it won't dispatch a new one
until the previous completes.

Change-Id: I8384bd475a1a004c567a4ae20ea64385244f45c5
parent 924dd555
Loading
Loading
Loading
Loading
+15 −23
Original line number Diff line number Diff line
@@ -537,9 +537,11 @@ public final class ActivityManagerService extends ActivityManagerNative
    final ActivityStarter mActivityStarter;
    /** Task stack change listeners. */
    private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
    private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
            new RemoteCallbackList<ITaskStackListener>();
    final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
    public IntentFirewall mIntentFirewall;
    // Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -17796,17 +17798,17 @@ public final class ActivityManagerService extends ActivityManagerNative
            } catch (RemoteException e) {
            }
            if (ii == null) {
                reportStartInstrumentationFailure(watcher, className,
                reportStartInstrumentationFailureLocked(watcher, className,
                        "Unable to find instrumentation info for: " + className);
                return false;
            }
            if (ai == null) {
                reportStartInstrumentationFailure(watcher, className,
                reportStartInstrumentationFailureLocked(watcher, className,
                        "Unable to find instrumentation target package: " + ii.targetPackage);
                return false;
            }
            if (!ai.hasCode()) {
                reportStartInstrumentationFailure(watcher, className,
                reportStartInstrumentationFailureLocked(watcher, className,
                        "Instrumentation target has no code: " + ii.targetPackage);
                return false;
            }
@@ -17821,7 +17823,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                        + " not allowed because package " + ii.packageName
                        + " does not have a signature matching the target "
                        + ii.targetPackage;
                reportStartInstrumentationFailure(watcher, className, msg);
                reportStartInstrumentationFailureLocked(watcher, className, msg);
                throw new SecurityException(msg);
            }
@@ -17852,31 +17854,21 @@ public final class ActivityManagerService extends ActivityManagerNative
     * @param cn The component name of the instrumentation.
     * @param report The error report.
     */
    private void reportStartInstrumentationFailure(IInstrumentationWatcher watcher,
    private void reportStartInstrumentationFailureLocked(IInstrumentationWatcher watcher,
            ComponentName cn, String report) {
        Slog.w(TAG, report);
        try {
        if (watcher != null) {
            Bundle results = new Bundle();
            results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService");
            results.putString("Error", report);
                watcher.instrumentationStatus(cn, -1, results);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, e);
            mInstrumentationReporter.reportStatus(watcher, cn, -1, results);
        }
    }
    void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
        if (app.instrumentationWatcher != null) {
            try {
                // NOTE:  IInstrumentationWatcher *must* be oneway here
                app.instrumentationWatcher.instrumentationFinished(
                    app.instrumentationClass,
                    resultCode,
                    results);
            } catch (RemoteException e) {
            }
            mInstrumentationReporter.reportFinished(app.instrumentationWatcher,
                    app.instrumentationClass, resultCode, results);
        }
        // Can't call out of the system process with a lock held, so post a message.
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.am;

import android.app.IInstrumentationWatcher;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;

import java.util.ArrayList;

public class InstrumentationReporter {
    static final boolean DEBUG = false;
    static final String TAG = ActivityManagerDebugConfig.TAG_AM;

    static final int REPORT_TYPE_STATUS = 0;
    static final int REPORT_TYPE_FINISHED = 1;

    final Object mLock = new Object();
    ArrayList<Report> mPendingReports;
    Thread mThread;

    final class MyThread extends Thread {
        public MyThread() {
            super("InstrumentationReporter");
            if (DEBUG) Slog.d(TAG, "Starting InstrumentationReporter: " + this);
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
            boolean waited = false;
            while (true) {
                ArrayList<Report> reports;
                synchronized (mLock) {
                    reports = mPendingReports;
                    mPendingReports = null;
                    if (reports == null || reports.isEmpty()) {
                        if (!waited) {
                            // Sleep for a little bit, to avoid thrashing through threads.
                            try {
                                mLock.wait(10000); // 10 seconds
                            } catch (InterruptedException e) {
                            }
                            waited = true;
                            continue;
                        } else {
                            mThread = null;
                            if (DEBUG) Slog.d(TAG, "Exiting InstrumentationReporter: " + this);
                            return;
                        }
                    }
                }

                waited = false;

                for (int i=0; i<reports.size(); i++) {
                    final Report rep = reports.get(i);
                    try {
                        if (rep.mType == REPORT_TYPE_STATUS) {
                            if (DEBUG) Slog.d(TAG, "Dispatch status to " + rep.mWatcher
                                    + ": " + rep.mName.flattenToShortString()
                                    + " code=" + rep.mResultCode + " result=" + rep.mResults);
                            rep.mWatcher.instrumentationStatus(rep.mName, rep.mResultCode,
                                    rep.mResults);
                        } else {
                            if (DEBUG) Slog.d(TAG, "Dispatch finished to " + rep.mWatcher
                                    + ": " + rep.mName.flattenToShortString()
                                    + " code=" + rep.mResultCode + " result=" + rep.mResults);
                            rep.mWatcher.instrumentationFinished(rep.mName, rep.mResultCode,
                                    rep.mResults);
                        }
                    } catch (RemoteException e) {
                        Slog.i(TAG, "Failure reporting to instrumentation watcher: comp="
                                + rep.mName + " results=" + rep.mResults);
                    }
                }
            }
        }
    }

    final class Report {
        final int mType;
        final IInstrumentationWatcher mWatcher;
        final ComponentName mName;
        final int mResultCode;
        final Bundle mResults;

        Report(int type, IInstrumentationWatcher watcher, ComponentName name, int resultCode,
                Bundle results) {
            mType = type;
            mWatcher = watcher;
            mName = name;
            mResultCode = resultCode;
            mResults = results;
        }
    }

    public void reportStatus(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
            Bundle results) {
        if (DEBUG) Slog.d(TAG, "Report status to " + watcher
                + ": " + name.flattenToShortString()
                + " code=" + resultCode + " result=" + results);
        report(new Report(REPORT_TYPE_STATUS, watcher, name, resultCode, results));
    }

    public void reportFinished(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
            Bundle results) {
        if (DEBUG) Slog.d(TAG, "Report finished to " + watcher
                + ": " + name.flattenToShortString()
                + " code=" + resultCode + " result=" + results);
        report(new Report(REPORT_TYPE_FINISHED, watcher, name, resultCode, results));
    }

    private void report(Report report) {
        synchronized (mLock) {
            if (mThread == null) {
                mThread = new MyThread();
                mThread.start();
            }
            if (mPendingReports == null) {
                mPendingReports = new ArrayList<>();
            }
            mPendingReports.add(report);
            mLock.notifyAll();
        }
    }
}