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