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