• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.car;
17 
18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
19 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU;
20 
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.car.builtin.app.ActivityManagerHelper;
24 import android.car.builtin.app.ActivityManagerHelper.ProcessObserverCallback;
25 import android.car.builtin.os.ProcessHelper;
26 import android.car.builtin.os.UserManagerHelper;
27 import android.car.builtin.util.Slogf;
28 import android.content.Context;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.util.ArrayMap;
34 import android.util.ArraySet;
35 import android.util.Log;
36 
37 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
38 import com.android.car.internal.util.IndentingPrintWriter;
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.lang.ref.WeakReference;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 
49 /**
50  * Service to monitor AMS for new Activity or Service launching.
51  */
52 public class SystemActivityMonitoringService implements CarServiceBase {
53 
54     /** Injector for injecting some system related operations. */
55     @VisibleForTesting
56     /* package */ interface Injector {
registerProcessObserverCallback(ProcessObserverCallback callback)57         void registerProcessObserverCallback(ProcessObserverCallback callback);
unregisterProcessObserverCallback(ProcessObserverCallback callback)58         void unregisterProcessObserverCallback(ProcessObserverCallback callback);
getPassengerActivitySetProcessGroupRetryTimeoutMs()59         long getPassengerActivitySetProcessGroupRetryTimeoutMs();
60     }
61 
62     // Passenger Activity might not be in top-app group in the 1st try. In that case, try
63     // again after this time. Retry will happen only once.
64     private static final long PASSENGER_ACTIVITY_SET_PROCESS_GROUP_RETRY_MS = 2_000;
65 
66     private final ProcessObserverCallback mProcessObserver = new ProcessObserver();
67 
68     private final HandlerThread mMonitorHandlerThread = CarServiceUtils.getHandlerThread(
69             getClass().getSimpleName());
70     private final ActivityMonitorHandler mHandler = new ActivityMonitorHandler(
71             mMonitorHandlerThread.getLooper(), this);
72 
73     private final Context mContext;
74 
75     private final Injector mInjector;
76 
77     private final Object mLock = new Object();
78 
79     @GuardedBy("mLock")
80     private final Map<Integer, Set<Integer>> mForegroundUidPids = new ArrayMap<>();
81 
82     @GuardedBy("mLock")
83     private final List<ProcessObserverCallback> mCustomProcessObservers = new ArrayList<>();
84 
85     @GuardedBy("mLock")
86     private boolean mAssignPassengerActivityToFgGroup;
87 
SystemActivityMonitoringService(Context context)88     public SystemActivityMonitoringService(Context context) {
89         this(context, new DefaultInjector());
90     }
91 
92     @VisibleForTesting
SystemActivityMonitoringService(Context context, Injector injector)93     /*package*/ SystemActivityMonitoringService(Context context, Injector injector) {
94         mContext = context;
95         mInjector = injector;
96     }
97 
98     @Override
init()99     public void init() {
100         boolean assignPassengerActivityToFgGroup = false;
101         if (isPlatformVersionAtLeastU()) {
102             if (mContext.getResources().getBoolean(
103                     R.bool.config_assignPassengerActivityToForegroundCpuGroup)) {
104                 CarOccupantZoneService occupantService = CarLocalServices.getService(
105                         CarOccupantZoneService.class);
106                 if (occupantService.hasDriverZone() && occupantService.hasPassengerZones()) {
107                     assignPassengerActivityToFgGroup = true;
108                 }
109             }
110         }
111         synchronized (mLock) {
112             mAssignPassengerActivityToFgGroup = assignPassengerActivityToFgGroup;
113         }
114         // Monitoring both listeners are necessary as there are cases where one listener cannot
115         // monitor activity change.
116         mInjector.registerProcessObserverCallback(mProcessObserver);
117     }
118 
119     @Override
release()120     public void release() {
121         mInjector.unregisterProcessObserverCallback(mProcessObserver);
122     }
123 
124     @Override
125     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)126     public void dump(IndentingPrintWriter writer) {
127         writer.println("*SystemActivityMonitoringService*");
128         writer.println(" Top Tasks per display:");
129         synchronized (mLock) {
130             writer.println(" Foreground uid-pids:");
131             for (Integer key : mForegroundUidPids.keySet()) {
132                 Set<Integer> pids = mForegroundUidPids.get(key);
133                 if (pids == null) {
134                     continue;
135                 }
136                 writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
137             }
138             writer.println(
139                     "mAssignPassengerActivityToFgGroup:" + mAssignPassengerActivityToFgGroup);
140         }
141     }
142 
143     /**
144      * Returns {@code true} if given pid-uid pair is in foreground.
145      */
isInForeground(int pid, int uid)146     public boolean isInForeground(int pid, int uid) {
147         Set<Integer> pids = getPidsOfForegroudApp(uid);
148         if (pids == null) {
149             return false;
150         }
151         return pids.contains(pid);
152     }
153 
154     /**
155      * Returns PIDs of foreground apps launched from the given UID.
156      */
157     @Nullable
getPidsOfForegroudApp(int uid)158     public Set<Integer> getPidsOfForegroudApp(int uid) {
159         synchronized (mLock) {
160             return mForegroundUidPids.get(uid);
161         }
162     }
163 
164     /** Registers a callback to get notified when the running state of a process has changed. */
registerProcessObserverCallback(ProcessObserverCallback callback)165     public void registerProcessObserverCallback(ProcessObserverCallback callback) {
166         synchronized (mLock) {
167             mCustomProcessObservers.add(callback);
168         }
169     }
170 
171     /** Unregisters the ProcessObserverCallback, if there is any. */
unregisterProcessObserverCallback(ProcessObserverCallback callback)172     public void unregisterProcessObserverCallback(ProcessObserverCallback callback) {
173         synchronized (mLock) {
174             mCustomProcessObservers.remove(callback);
175         }
176     }
177 
handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)178     private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
179         synchronized (mLock) {
180             for (int i = 0; i < mCustomProcessObservers.size(); i++) {
181                 ProcessObserverCallback callback = mCustomProcessObservers.get(i);
182                 callback.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
183             }
184             if (foregroundActivities) {
185                 Set<Integer> pids = mForegroundUidPids.get(uid);
186                 if (pids == null) {
187                     pids = new ArraySet<Integer>();
188                     mForegroundUidPids.put(uid, pids);
189                 }
190                 pids.add(pid);
191             } else {
192                 doHandlePidGoneLocked(pid, uid);
193             }
194         }
195     }
196 
handleProcessDied(int pid, int uid)197     private void handleProcessDied(int pid, int uid) {
198         synchronized (mLock) {
199             for (int i = 0; i < mCustomProcessObservers.size(); i++) {
200                 ProcessObserverCallback callback = mCustomProcessObservers.get(i);
201                 callback.onProcessDied(pid, uid);
202             }
203             doHandlePidGoneLocked(pid, uid);
204         }
205     }
206 
207     @GuardedBy("mLock")
doHandlePidGoneLocked(int pid, int uid)208     private void doHandlePidGoneLocked(int pid, int uid) {
209         Set<Integer> pids = mForegroundUidPids.get(uid);
210         if (pids != null) {
211             pids.remove(pid);
212             if (pids.isEmpty()) {
213                 mForegroundUidPids.remove(uid);
214             }
215         }
216     }
217 
218     /**
219      * Updates the process group for given PID if it is passenger app and returns true if it should
220      * be retried.
221      */
doHandleProcessGroupForFgApp(int pid, int uid)222     private boolean doHandleProcessGroupForFgApp(int pid, int uid) {
223         synchronized (mLock) {
224             if (!mAssignPassengerActivityToFgGroup) {
225                 return false;
226             }
227         }
228         int userId = UserManagerHelper.getUserId(uid);
229         // Current user will be driver. So do not touch it.
230         // User 0 will be either current user or common system UI which should run with higher
231         // priority.
232         if (userId == ActivityManager.getCurrentUser() || userId == UserManagerHelper.USER_SYSTEM) {
233             return false;
234         }
235         // TODO(b/261783537) ignore profile of the current user
236 
237         CarServiceHelperWrapper helper = CarServiceHelperWrapper.getInstance();
238         boolean shouldRetry = false;
239         try {
240             int processGroup = helper.getProcessGroup(pid);
241             switch (processGroup) {
242                 case ProcessHelper.THREAD_GROUP_FOREGROUND:
243                     // already in FG group, ignore
244                     break;
245                 case ProcessHelper.THREAD_GROUP_TOP_APP:
246                     // Changing to FOREGROUND requires setting it to DEFAULT
247                     helper.setProcessGroup(pid, ProcessHelper.THREAD_GROUP_DEFAULT);
248                     break;
249                 default:
250                     // not in top-app yet, should retry
251                     shouldRetry = true;
252                     break;
253             }
254         } catch (Exception e) {
255             Slogf.w(CarLog.TAG_AM, e, "Process group manipulation failed for pid:%d uid:%d",
256                     pid, uid);
257             // no need to retry as this PID may be already invalid.
258         }
259         return shouldRetry;
260     }
261 
handleProcessGroupForFgApp(int pid, int uid)262     private void handleProcessGroupForFgApp(int pid, int uid) {
263         if (doHandleProcessGroupForFgApp(pid, uid)) {
264             mHandler.postDelayed(() -> doHandleProcessGroupForFgApp(pid, uid),
265                     mInjector.getPassengerActivitySetProcessGroupRetryTimeoutMs());
266         }
267     }
268 
269     private class ProcessObserver extends ProcessObserverCallback {
270         @Override
onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)271         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
272             if (Slogf.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
273                 Slogf.d(CarLog.TAG_AM,
274                         String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
275                                 uid, pid, foregroundActivities));
276             }
277             if (foregroundActivities) {
278                 handleProcessGroupForFgApp(pid, uid);
279             }
280             mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
281         }
282 
283         @Override
onProcessDied(int pid, int uid)284         public void onProcessDied(int pid, int uid) {
285             mHandler.requestProcessDied(pid, uid);
286         }
287     }
288 
289     private static final class ActivityMonitorHandler extends Handler {
290         private static final String TAG = ActivityMonitorHandler.class.getSimpleName();
291 
292         private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
293         private static final int MSG_PROCESS_DIED = 2;
294 
295         private final WeakReference<SystemActivityMonitoringService> mService;
296 
ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service)297         private ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service) {
298             super(looper);
299             mService = new WeakReference<SystemActivityMonitoringService>(service);
300         }
301 
requestForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)302         private void requestForegroundActivitiesChanged(int pid, int uid,
303                 boolean foregroundActivities) {
304             Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
305                     Boolean.valueOf(foregroundActivities));
306             sendMessage(msg);
307         }
308 
requestProcessDied(int pid, int uid)309         private void requestProcessDied(int pid, int uid) {
310             Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
311             sendMessage(msg);
312         }
313 
314         @Override
handleMessage(Message msg)315         public void handleMessage(Message msg) {
316             SystemActivityMonitoringService service = mService.get();
317             if (service == null) {
318                 Slogf.i(TAG, "handleMessage null service");
319                 return;
320             }
321             switch (msg.what) {
322                 case MSG_FOREGROUND_ACTIVITIES_CHANGED:
323                     service.handleForegroundActivitiesChanged(msg.arg1, msg.arg2,
324                             (Boolean) msg.obj);
325                     break;
326                 case MSG_PROCESS_DIED:
327                     service.handleProcessDied(msg.arg1, msg.arg2);
328                     break;
329                 default:
330                     break;
331             }
332         }
333     }
334 
335     private static class DefaultInjector implements Injector {
336         @Override
registerProcessObserverCallback(ProcessObserverCallback callback)337         public void registerProcessObserverCallback(ProcessObserverCallback callback) {
338             ActivityManagerHelper.registerProcessObserverCallback(callback);
339         }
340 
341         @Override
unregisterProcessObserverCallback(ProcessObserverCallback callback)342         public void unregisterProcessObserverCallback(ProcessObserverCallback callback) {
343             ActivityManagerHelper.unregisterProcessObserverCallback(callback);
344         }
345 
346         @Override
getPassengerActivitySetProcessGroupRetryTimeoutMs()347         public long getPassengerActivitySetProcessGroupRetryTimeoutMs() {
348             return PASSENGER_ACTIVITY_SET_PROCESS_GROUP_RETRY_MS;
349         }
350     }
351 }
352