/* * Copyright 2017 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.app.servertransaction; import static android.app.ActivityThread.DEBUG_MEMORY_TRIM; import android.app.ActivityClient; import android.app.ActivityThread.ActivityClientRecord; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; import android.os.TransactionTooLargeException; import android.util.Log; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import java.io.StringWriter; /** * Container that has data pending to be used at later stages of * {@link android.app.servertransaction.ClientTransaction}. * An instance of this class is passed to each individual transaction item, so it can use some * information from previous steps or add some for the following steps. * * @hide */ public class PendingTransactionActions { private boolean mRestoreInstanceState; private boolean mCallOnPostCreate; private Bundle mOldState; private StopInfo mStopInfo; public PendingTransactionActions() { clear(); } /** Reset the state of the instance to default, non-initialized values. */ public void clear() { mRestoreInstanceState = false; mCallOnPostCreate = false; mOldState = null; mStopInfo = null; } /** Getter */ public boolean shouldRestoreInstanceState() { return mRestoreInstanceState; } public void setRestoreInstanceState(boolean restoreInstanceState) { mRestoreInstanceState = restoreInstanceState; } /** Getter */ public boolean shouldCallOnPostCreate() { return mCallOnPostCreate; } public void setCallOnPostCreate(boolean callOnPostCreate) { mCallOnPostCreate = callOnPostCreate; } public Bundle getOldState() { return mOldState; } public void setOldState(Bundle oldState) { mOldState = oldState; } public StopInfo getStopInfo() { return mStopInfo; } public void setStopInfo(StopInfo stopInfo) { mStopInfo = stopInfo; } /** Reports to server about activity stop. */ public static class StopInfo implements Runnable { private static final String TAG = "ActivityStopInfo"; private ActivityClientRecord mActivity; private Bundle mState; private PersistableBundle mPersistentState; private CharSequence mDescription; public void setActivity(ActivityClientRecord activity) { mActivity = activity; } public void setState(Bundle state) { mState = state; } public void setPersistentState(PersistableBundle persistentState) { mPersistentState = persistentState; } public void setDescription(CharSequence description) { mDescription = description; } private String collectBundleStates() { final StringWriter writer = new StringWriter(); final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println("Bundle stats:"); Bundle.dumpStats(pw, mState); pw.println("PersistableBundle stats:"); Bundle.dumpStats(pw, mPersistentState); return writer.toString().stripTrailing(); } @Override public void run() { // Tell activity manager we have been stopped. try { if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity); // TODO(lifecycler): Use interface callback instead of AMS. ActivityClient.getInstance().activityStopped( mActivity.token, mState, mPersistentState, mDescription); } catch (RuntimeException runtimeException) { // Collect the statistics about bundle final String bundleStats = collectBundleStates(); RuntimeException ex = runtimeException; if (ex.getCause() instanceof TransactionTooLargeException) { // Embed the stats into exception message to help developers debug if the // transaction size is too large. final String message = ex.getMessage() + "\n" + bundleStats; ex = new RuntimeException(message, ex.getCause()); if (mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) { Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex); return; } } else { // Otherwise, dump the stats anyway. Log.w(TAG, bundleStats); } throw ex; } } } }