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.LogWriter;
29 import android.util.Slog;
30 
31 import com.android.internal.util.IndentingPrintWriter;
32 
33 /**
34  * Container that has data pending to be used at later stages of
35  * {@link android.app.servertransaction.ClientTransaction}.
36  * An instance of this class is passed to each individual transaction item, so it can use some
37  * information from previous steps or add some for the following steps.
38  *
39  * @hide
40  */
41 public class PendingTransactionActions {
42     private boolean mRestoreInstanceState;
43     private boolean mCallOnPostCreate;
44     private Bundle mOldState;
45     private StopInfo mStopInfo;
46     private boolean mReportRelaunchToWM;
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     /**
95      * Check if we should report an activity relaunch to WindowManager. We report back for every
96      * relaunch request to ActivityManager, but only for those that were actually finished to we
97      * report to WindowManager.
98      */
shouldReportRelaunchToWindowManager()99     public boolean shouldReportRelaunchToWindowManager() {
100         return mReportRelaunchToWM;
101     }
102 
103     /**
104      * Set if we should report an activity relaunch to WindowManager. We report back for every
105      * relaunch request to ActivityManager, but only for those that were actually finished we report
106      * to WindowManager.
107      */
setReportRelaunchToWindowManager(boolean reportToWm)108     public void setReportRelaunchToWindowManager(boolean reportToWm) {
109         mReportRelaunchToWM = reportToWm;
110     }
111 
112     /** Reports to server about activity stop. */
113     public static class StopInfo implements Runnable {
114         private static final String TAG = "ActivityStopInfo";
115 
116         private ActivityClientRecord mActivity;
117         private Bundle mState;
118         private PersistableBundle mPersistentState;
119         private CharSequence mDescription;
120 
setActivity(ActivityClientRecord activity)121         public void setActivity(ActivityClientRecord activity) {
122             mActivity = activity;
123         }
124 
setState(Bundle state)125         public void setState(Bundle state) {
126             mState = state;
127         }
128 
setPersistentState(PersistableBundle persistentState)129         public void setPersistentState(PersistableBundle persistentState) {
130             mPersistentState = persistentState;
131         }
132 
setDescription(CharSequence description)133         public void setDescription(CharSequence description) {
134             mDescription = description;
135         }
136 
137         @Override
run()138         public void run() {
139             // Tell activity manager we have been stopped.
140             try {
141                 if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
142                 // TODO(lifecycler): Use interface callback instead of AMS.
143                 ActivityClient.getInstance().activityStopped(
144                         mActivity.token, mState, mPersistentState, mDescription);
145             } catch (RuntimeException ex) {
146                 // Dump statistics about bundle to help developers debug
147                 final LogWriter writer = new LogWriter(Log.WARN, TAG);
148                 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
149                 pw.println("Bundle stats:");
150                 Bundle.dumpStats(pw, mState);
151                 pw.println("PersistableBundle stats:");
152                 Bundle.dumpStats(pw, mPersistentState);
153 
154                 if (ex.getCause() instanceof TransactionTooLargeException
155                         && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
156                     Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
157                     return;
158                 }
159                 throw ex;
160             }
161         }
162     }
163 }
164