• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 com.android.server.am;
18 
19 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
20 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
21 
22 import android.annotation.IntDef;
23 import android.app.IApplicationThread;
24 import android.os.RemoteException;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.Arrays;
32 
33 /**
34  * A subclass of {@link IApplicationThread} that defers certain binder calls while the process is
35  * paused (frozen).  Any deferred calls are executed when the process is unpaused.  In some cases,
36  * multiple instances of deferred calls are collapsed into a single call when the process is
37  * unpaused.
38  *
39  * {@hide}
40  */
41 final class ApplicationThreadDeferred extends IApplicationThread.Delegator {
42 
43     static final String TAG = TAG_WITH_CLASS_NAME ? "ApplicationThreadDeferred" : TAG_AM;
44 
45     // The flag that enables the deferral behavior of this class.  If the flag is disabled then
46     // the class behaves exactly like an ApplicationThreadFilter.
deferBindersWhenPaused()47     private static boolean deferBindersWhenPaused() {
48         return Flags.deferBindersWhenPaused();
49     }
50 
51     // The list of notifications that may be deferred.
52     private static final int CLEAR_DNS_CACHE = 0;
53     private static final int UPDATE_TIME_ZONE = 1;
54     private static final int SCHEDULE_LOW_MEMORY = 2;
55     private static final int UPDATE_HTTP_PROXY = 3;
56     private static final int NOTIFICATION_COUNT = 4;
57 
58     @IntDef(value = {
59                 CLEAR_DNS_CACHE,
60                 UPDATE_TIME_ZONE,
61                 SCHEDULE_LOW_MEMORY,
62                 UPDATE_HTTP_PROXY
63             })
64     @Retention(RetentionPolicy.SOURCE)
65     private @interface NotificationType {};
66 
67     private final Object mLock = new Object();
68 
69     // If this is true, notifications should be queued for later delivery.  If this is false,
70     // notifications should be delivered immediately.
71     @GuardedBy("mLock")
72     private boolean mPaused = false;
73 
74     // An operation is a lambda that throws an exception.
75     private interface Operation {
run()76         void run() throws RemoteException;
77     }
78 
79     // The array of operations.
80     @GuardedBy("mLock")
81     private final Operation[] mOperations = new Operation[NOTIFICATION_COUNT];
82 
83     // The array of operations that actually pending right now.
84     @GuardedBy("mLock")
85     private final boolean[] mPending = new boolean[NOTIFICATION_COUNT];
86 
87     // When true, binder calls to paused processes will be deferred until the process is unpaused.
88     private final boolean mDefer;
89 
90     // The base thread, because Delegator does not expose it.
91     private final IApplicationThread mBase;
92 
93     /** Create an instance with a base thread and a deferral enable flag. */
94     @VisibleForTesting
ApplicationThreadDeferred(IApplicationThread thread, boolean defer)95     public ApplicationThreadDeferred(IApplicationThread thread, boolean defer) {
96         super(thread);
97 
98         mBase = thread;
99         mDefer = defer;
100 
101         mOperations[CLEAR_DNS_CACHE] = () -> { super.clearDnsCache(); };
102         mOperations[UPDATE_TIME_ZONE] = () -> { super.updateTimeZone(); };
103         mOperations[SCHEDULE_LOW_MEMORY] = () -> { super.scheduleLowMemory(); };
104         mOperations[UPDATE_HTTP_PROXY] = () -> { super.updateHttpProxy(); };
105     }
106 
107     /** Create an instance with a base flag, using the system deferral enable flag. */
ApplicationThreadDeferred(IApplicationThread thread)108     public ApplicationThreadDeferred(IApplicationThread thread) {
109         this(thread, deferBindersWhenPaused());
110     }
111 
112     /**
113      * Return the implementation's value of asBinder(). super.asBinder() is not a real Binder
114      * object.
115      */
116     @Override
asBinder()117     public  android.os.IBinder asBinder() {
118         return mBase.asBinder();
119     }
120 
121     /** The process is being paused.  Start deferring calls. */
onProcessPaused()122     void onProcessPaused() {
123         synchronized (mLock) {
124             mPaused = true;
125         }
126     }
127 
128     /** The process is no longer paused.  Drain any deferred calls. */
onProcessUnpaused()129     void onProcessUnpaused() {
130         synchronized (mLock) {
131             mPaused = false;
132             try {
133                 for (int i = 0; i < mOperations.length; i++) {
134                     if (mPending[i]) {
135                         mOperations[i].run();
136                     }
137                 }
138             } catch (RemoteException e) {
139                 // Swallow the exception.  The caller is not expecting it.  Remote exceptions
140                 // happen if a has process died; there is no need to report it here.
141             } finally {
142                 Arrays.fill(mPending, false);
143             }
144         }
145     }
146 
147     /** The pause operation has been canceled.  Drain any deferred calls. */
onProcessPausedCancelled()148     void onProcessPausedCancelled() {
149         onProcessUnpaused();
150     }
151 
152     /**
153      * If the thread is not paused, execute the operation.  Otherwise, save it to the pending
154      * list.
155      */
execute(@otificationType int tag)156     private void execute(@NotificationType int tag) throws RemoteException {
157         synchronized (mLock) {
158             if (mPaused && mDefer) {
159                 mPending[tag] = true;
160                 return;
161             }
162         }
163         // Outside the synchronization block to avoid contention.
164         mOperations[tag].run();
165     }
166 
167     @Override
clearDnsCache()168     public void clearDnsCache() throws RemoteException {
169         execute(CLEAR_DNS_CACHE);
170     }
171 
172     @Override
updateTimeZone()173     public void updateTimeZone() throws RemoteException {
174         execute(UPDATE_TIME_ZONE);
175     }
176 
177     @Override
scheduleLowMemory()178     public void scheduleLowMemory() throws RemoteException {
179         execute(SCHEDULE_LOW_MEMORY);
180     }
181 
182     @Override
updateHttpProxy()183     public void updateHttpProxy() throws RemoteException {
184         execute(UPDATE_HTTP_PROXY);
185     }
186 }
187