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