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 synchronized (mLock) { 206 for (int i = 0; i < mCustomProcessObservers.size(); i++) { 207 ProcessObserverCallback callback = mCustomProcessObservers.get(i); 208 callback.onProcessDied(pid, uid); 209 } 210 doHandlePidGoneLocked(pid, uid); 211 } 212 } 213 214 @GuardedBy("mLock") doHandlePidGoneLocked(int pid, int uid)215 private void doHandlePidGoneLocked(int pid, int uid) { 216 Set<Integer> pids = mForegroundUidPids.get(uid); 217 if (pids != null) { 218 pids.remove(pid); 219 if (pids.isEmpty()) { 220 mForegroundUidPids.remove(uid); 221 } 222 } 223 } 224 225 /** 226 * Updates the process group for given PID if it is passenger app and returns true if it should 227 * be retried. 228 */ updateProcessGroupForFgApp(int pid, int uid)229 private boolean updateProcessGroupForFgApp(int pid, int uid) { 230 int driverTopAppPid; 231 synchronized (mLock) { 232 if (!mAssignPassengerActivityToFgGroup) { 233 return false; 234 } 235 int userId = UserManagerHelper.getUserId(uid); 236 // Current user will be driver. So do not touch it. 237 // User 0 will be either current user or common system UI which should run with higher 238 // priority. 239 if (userId == ActivityManager.getCurrentUser() 240 || userId == UserManagerHelper.USER_SYSTEM) { 241 mDriverTopAppPid = pid; 242 return false; 243 } 244 driverTopAppPid = mDriverTopAppPid; 245 } 246 // TODO(b/261783537) ignore profile of the current user 247 248 CarServiceHelperWrapper helper = CarServiceHelperWrapper.getInstance(); 249 boolean shouldRetry = false; 250 try { 251 int processGroup = helper.getProcessGroup(pid); 252 if (DBG) { 253 Slogf.d(CarLog.TAG_AM, "doHandleProcessGroupForFgApp: pid=%d pGroup=%d", 254 pid, processGroup); 255 } 256 switch (processGroup) { 257 case ProcessHelper.THREAD_GROUP_FOREGROUND: 258 // SetProcessGroup happens in OomAdjuster#mProcessGroupHandler in System 259 // Server, but which is the different thread with the main thread of 260 // OomAdjuster, and the focus change event is propagated to CarService 261 // through Binder, so there is race-condition between setting ProcessGroup in 262 // System Server and here. 263 // So, there are chances that to set Top App is not executed yet. 264 shouldRetry = true; 265 break; 266 case ProcessHelper.THREAD_GROUP_TOP_APP: 267 // Changing to FOREGROUND requires setting it to DEFAULT 268 helper.setProcessGroup(pid, ProcessHelper.THREAD_GROUP_DEFAULT); 269 if (driverTopAppPid != Process.INVALID_PID) { 270 helper.setProcessGroup(driverTopAppPid, ProcessHelper.THREAD_GROUP_TOP_APP); 271 } 272 break; 273 default: 274 // not in top-app yet, should retry 275 shouldRetry = true; 276 break; 277 } 278 } catch (Exception e) { 279 Slogf.w(CarLog.TAG_AM, e, "Process group manipulation failed for pid:%d uid:%d", 280 pid, uid); 281 // no need to retry as this PID may be already invalid. 282 } 283 return shouldRetry; 284 } 285 handleProcessGroupForFgApp(int pid, int uid)286 private void handleProcessGroupForFgApp(int pid, int uid) { 287 if (updateProcessGroupForFgApp(pid, uid)) { 288 if (DBG) { 289 Slogf.d(CarLog.TAG_AM, "Will retry handleProcessGroupForFgApp: pid=%d, uid=%d", 290 pid, uid); 291 } 292 mHandler.postDelayed(() -> updateProcessGroupForFgApp(pid, uid), 293 mInjector.getPassengerActivitySetProcessGroupRetryTimeoutMs()); 294 } 295 } 296 handleProcessGroupForDiedApp(int pid)297 private void handleProcessGroupForDiedApp(int pid) { 298 synchronized (mLock) { 299 if (!mAssignPassengerActivityToFgGroup) { 300 return; 301 } 302 if (pid == mDriverTopAppPid) { 303 mDriverTopAppPid = Process.INVALID_PID; 304 } 305 } 306 } 307 308 /** Handles focusChanged event. */ handleFocusChanged(int pid, int uid)309 public void handleFocusChanged(int pid, int uid) { 310 if (DBG) Slogf.d(CarLog.TAG_AM, "notifyFocusChanged: pid=%d uid=%d", pid, uid); 311 handleProcessGroupForFgApp(pid, uid); 312 } 313 314 private class ProcessObserver extends ProcessObserverCallback { 315 @Override onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)316 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) { 317 if (Slogf.isLoggable(CarLog.TAG_AM, Log.DEBUG)) { 318 Slogf.d(CarLog.TAG_AM, 319 String.format("onForegroundActivitiesChanged uid %d pid %d fg %b", 320 uid, pid, foregroundActivities)); 321 } 322 mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities); 323 } 324 325 @Override onProcessDied(int pid, int uid)326 public void onProcessDied(int pid, int uid) { 327 handleProcessGroupForDiedApp(pid); 328 mHandler.requestProcessDied(pid, uid); 329 } 330 } 331 332 private static final class ActivityMonitorHandler extends Handler { 333 private static final String TAG = ActivityMonitorHandler.class.getSimpleName(); 334 335 private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1; 336 private static final int MSG_PROCESS_DIED = 2; 337 338 private final WeakReference<SystemActivityMonitoringService> mService; 339 ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service)340 private ActivityMonitorHandler(Looper looper, SystemActivityMonitoringService service) { 341 super(looper); 342 mService = new WeakReference<SystemActivityMonitoringService>(service); 343 } 344 requestForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)345 private void requestForegroundActivitiesChanged(int pid, int uid, 346 boolean foregroundActivities) { 347 Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid, 348 Boolean.valueOf(foregroundActivities)); 349 sendMessage(msg); 350 } 351 requestProcessDied(int pid, int uid)352 private void requestProcessDied(int pid, int uid) { 353 Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid); 354 sendMessage(msg); 355 } 356 357 @Override handleMessage(Message msg)358 public void handleMessage(Message msg) { 359 SystemActivityMonitoringService service = mService.get(); 360 if (service == null) { 361 Slogf.i(TAG, "handleMessage null service"); 362 return; 363 } 364 switch (msg.what) { 365 case MSG_FOREGROUND_ACTIVITIES_CHANGED: 366 service.handleForegroundActivitiesChanged(msg.arg1, msg.arg2, 367 (Boolean) msg.obj); 368 break; 369 case MSG_PROCESS_DIED: 370 service.handleProcessDied(msg.arg1, msg.arg2); 371 break; 372 default: 373 break; 374 } 375 } 376 } 377 378 private static class DefaultInjector implements Injector { 379 @Override registerProcessObserverCallback(ProcessObserverCallback callback)380 public void registerProcessObserverCallback(ProcessObserverCallback callback) { 381 ActivityManagerHelper.registerProcessObserverCallback(callback); 382 } 383 384 @Override unregisterProcessObserverCallback(ProcessObserverCallback callback)385 public void unregisterProcessObserverCallback(ProcessObserverCallback callback) { 386 ActivityManagerHelper.unregisterProcessObserverCallback(callback); 387 } 388 389 @Override getPassengerActivitySetProcessGroupRetryTimeoutMs()390 public long getPassengerActivitySetProcessGroupRetryTimeoutMs() { 391 return PASSENGER_ACTIVITY_SET_PROCESS_GROUP_RETRY_MS; 392 } 393 } 394 } 395