1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app.servertransaction;
18 
19 import static android.app.ActivityThread.DEBUG_MEMORY_TRIM;
20 
21 import android.app.ActivityClient;
22 import android.app.ActivityThread.ActivityClientRecord;
23 import android.os.Build;
24 import android.os.Bundle;
25 import android.os.PersistableBundle;
26 import android.os.TransactionTooLargeException;
27 import android.util.Log;
28 import android.util.Slog;
29 
30 import com.android.internal.util.IndentingPrintWriter;
31 
32 import java.io.StringWriter;
33 
34 /**
35  * Container that has data pending to be used at later stages of
36  * {@link android.app.servertransaction.ClientTransaction}.
37  * An instance of this class is passed to each individual transaction item, so it can use some
38  * information from previous steps or add some for the following steps.
39  *
40  * @hide
41  */
42 public class PendingTransactionActions {
43     private boolean mRestoreInstanceState;
44     private boolean mCallOnPostCreate;
45     private Bundle mOldState;
46     private StopInfo mStopInfo;
47 
PendingTransactionActions()48     public PendingTransactionActions() {
49         clear();
50     }
51 
52     /** Reset the state of the instance to default, non-initialized values. */
clear()53     public void clear() {
54         mRestoreInstanceState = false;
55         mCallOnPostCreate = false;
56         mOldState = null;
57         mStopInfo = null;
58     }
59 
60     /** Getter */
shouldRestoreInstanceState()61     public boolean shouldRestoreInstanceState() {
62         return mRestoreInstanceState;
63     }
64 
setRestoreInstanceState(boolean restoreInstanceState)65     public void setRestoreInstanceState(boolean restoreInstanceState) {
66         mRestoreInstanceState = restoreInstanceState;
67     }
68 
69     /** Getter */
shouldCallOnPostCreate()70     public boolean shouldCallOnPostCreate() {
71         return mCallOnPostCreate;
72     }
73 
setCallOnPostCreate(boolean callOnPostCreate)74     public void setCallOnPostCreate(boolean callOnPostCreate) {
75         mCallOnPostCreate = callOnPostCreate;
76     }
77 
getOldState()78     public Bundle getOldState() {
79         return mOldState;
80     }
81 
setOldState(Bundle oldState)82     public void setOldState(Bundle oldState) {
83         mOldState = oldState;
84     }
85 
getStopInfo()86     public StopInfo getStopInfo() {
87         return mStopInfo;
88     }
89 
setStopInfo(StopInfo stopInfo)90     public void setStopInfo(StopInfo stopInfo) {
91         mStopInfo = stopInfo;
92     }
93 
94     /** Reports to server about activity stop. */
95     public static class StopInfo implements Runnable {
96         private static final String TAG = "ActivityStopInfo";
97 
98         private ActivityClientRecord mActivity;
99         private Bundle mState;
100         private PersistableBundle mPersistentState;
101         private CharSequence mDescription;
102 
setActivity(ActivityClientRecord activity)103         public void setActivity(ActivityClientRecord activity) {
104             mActivity = activity;
105         }
106 
setState(Bundle state)107         public void setState(Bundle state) {
108             mState = state;
109         }
110 
setPersistentState(PersistableBundle persistentState)111         public void setPersistentState(PersistableBundle persistentState) {
112             mPersistentState = persistentState;
113         }
114 
setDescription(CharSequence description)115         public void setDescription(CharSequence description) {
116             mDescription = description;
117         }
118 
collectBundleStates()119         private String collectBundleStates() {
120             final StringWriter writer = new StringWriter();
121             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
122             pw.println("Bundle stats:");
123             Bundle.dumpStats(pw, mState);
124             pw.println("PersistableBundle stats:");
125             Bundle.dumpStats(pw, mPersistentState);
126             return writer.toString().stripTrailing();
127         }
128 
129         @Override
run()130         public void run() {
131             // Tell activity manager we have been stopped.
132             try {
133                 if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
134                 // TODO(lifecycler): Use interface callback instead of AMS.
135                 ActivityClient.getInstance().activityStopped(
136                         mActivity.token, mState, mPersistentState, mDescription);
137             } catch (RuntimeException runtimeException) {
138                 // Collect the statistics about bundle
139                 final String bundleStats = collectBundleStates();
140 
141                 RuntimeException ex = runtimeException;
142                 if (ex.getCause() instanceof TransactionTooLargeException) {
143                     // Embed the stats into exception message to help developers debug if the
144                     // transaction size is too large.
145                     final String message = ex.getMessage() + "\n" + bundleStats;
146                     ex = new RuntimeException(message, ex.getCause());
147                     if (mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
148                         Log.e(TAG, "App sent too much data in instance state, so it was ignored",
149                                 ex);
150                         return;
151                     }
152                 } else {
153                     // Otherwise, dump the stats anyway.
154                     Log.w(TAG, bundleStats);
155                 }
156                 throw ex;
157             }
158         }
159     }
160 }
161