• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     private boolean mReportRelaunchToWM;
48 
PendingTransactionActions()49     public PendingTransactionActions() {
50         clear();
51     }
52 
53     /** Reset the state of the instance to default, non-initialized values. */
clear()54     public void clear() {
55         mRestoreInstanceState = false;
56         mCallOnPostCreate = false;
57         mOldState = null;
58         mStopInfo = null;
59     }
60 
61     /** Getter */
shouldRestoreInstanceState()62     public boolean shouldRestoreInstanceState() {
63         return mRestoreInstanceState;
64     }
65 
setRestoreInstanceState(boolean restoreInstanceState)66     public void setRestoreInstanceState(boolean restoreInstanceState) {
67         mRestoreInstanceState = restoreInstanceState;
68     }
69 
70     /** Getter */
shouldCallOnPostCreate()71     public boolean shouldCallOnPostCreate() {
72         return mCallOnPostCreate;
73     }
74 
setCallOnPostCreate(boolean callOnPostCreate)75     public void setCallOnPostCreate(boolean callOnPostCreate) {
76         mCallOnPostCreate = callOnPostCreate;
77     }
78 
getOldState()79     public Bundle getOldState() {
80         return mOldState;
81     }
82 
setOldState(Bundle oldState)83     public void setOldState(Bundle oldState) {
84         mOldState = oldState;
85     }
86 
getStopInfo()87     public StopInfo getStopInfo() {
88         return mStopInfo;
89     }
90 
setStopInfo(StopInfo stopInfo)91     public void setStopInfo(StopInfo stopInfo) {
92         mStopInfo = stopInfo;
93     }
94 
95     /**
96      * Check if we should report an activity relaunch to WindowManager. We report back for every
97      * relaunch request to ActivityManager, but only for those that were actually finished to we
98      * report to WindowManager.
99      */
shouldReportRelaunchToWindowManager()100     public boolean shouldReportRelaunchToWindowManager() {
101         return mReportRelaunchToWM;
102     }
103 
104     /**
105      * Set if we should report an activity relaunch to WindowManager. We report back for every
106      * relaunch request to ActivityManager, but only for those that were actually finished we report
107      * to WindowManager.
108      */
setReportRelaunchToWindowManager(boolean reportToWm)109     public void setReportRelaunchToWindowManager(boolean reportToWm) {
110         mReportRelaunchToWM = reportToWm;
111     }
112 
113     /** Reports to server about activity stop. */
114     public static class StopInfo implements Runnable {
115         private static final String TAG = "ActivityStopInfo";
116 
117         private ActivityClientRecord mActivity;
118         private Bundle mState;
119         private PersistableBundle mPersistentState;
120         private CharSequence mDescription;
121 
setActivity(ActivityClientRecord activity)122         public void setActivity(ActivityClientRecord activity) {
123             mActivity = activity;
124         }
125 
setState(Bundle state)126         public void setState(Bundle state) {
127             mState = state;
128         }
129 
setPersistentState(PersistableBundle persistentState)130         public void setPersistentState(PersistableBundle persistentState) {
131             mPersistentState = persistentState;
132         }
133 
setDescription(CharSequence description)134         public void setDescription(CharSequence description) {
135             mDescription = description;
136         }
137 
collectBundleStates()138         private String collectBundleStates() {
139             final StringWriter writer = new StringWriter();
140             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
141             pw.println("Bundle stats:");
142             Bundle.dumpStats(pw, mState);
143             pw.println("PersistableBundle stats:");
144             Bundle.dumpStats(pw, mPersistentState);
145             return writer.toString().stripTrailing();
146         }
147 
148         @Override
run()149         public void run() {
150             // Tell activity manager we have been stopped.
151             try {
152                 if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
153                 // TODO(lifecycler): Use interface callback instead of AMS.
154                 ActivityClient.getInstance().activityStopped(
155                         mActivity.token, mState, mPersistentState, mDescription);
156             } catch (RuntimeException runtimeException) {
157                 // Collect the statistics about bundle
158                 final String bundleStats = collectBundleStates();
159 
160                 RuntimeException ex = runtimeException;
161                 if (ex.getCause() instanceof TransactionTooLargeException) {
162                     // Embed the stats into exception message to help developers debug if the
163                     // transaction size is too large.
164                     final String message = ex.getMessage() + "\n" + bundleStats;
165                     ex = new RuntimeException(message, ex.getCause());
166                     if (mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
167                         Log.e(TAG, "App sent too much data in instance state, so it was ignored",
168                                 ex);
169                         return;
170                     }
171                 } else {
172                     // Otherwise, dump the stats anyway.
173                     Log.w(TAG, bundleStats);
174                 }
175                 throw ex;
176             }
177         }
178     }
179 }
180