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