1 /*
2  * Copyright (C) 2023 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 
17 package com.android.car.occupantconnection;
18 
19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
20 import static android.car.CarOccupantZoneManager.INVALID_USER_ID;
21 import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_INSTALLED;
22 import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_IN_FOREGROUND;
23 import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_RUNNING;
24 import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_SAME_LONG_VERSION;
25 import static android.car.CarRemoteDeviceManager.FLAG_CLIENT_SAME_SIGNATURE;
26 import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_CONNECTION_READY;
27 import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_POWER_ON;
28 import static android.car.CarRemoteDeviceManager.FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED;
29 import static android.car.builtin.display.DisplayManagerHelper.EVENT_FLAG_DISPLAY_CHANGED;
30 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE;
31 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
32 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
33 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
34 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_VISIBLE;
35 import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
36 
37 import static com.android.car.CarServiceUtils.assertPermission;
38 import static com.android.car.CarServiceUtils.checkCalledByPackage;
39 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE;
40 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
41 
42 import android.annotation.IntDef;
43 import android.annotation.Nullable;
44 import android.app.ActivityManager;
45 import android.app.ActivityManager.RunningAppProcessInfo;
46 import android.car.Car;
47 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
48 import android.car.CarRemoteDeviceManager.AppState;
49 import android.car.CarRemoteDeviceManager.OccupantZoneState;
50 import android.car.builtin.app.ActivityManagerHelper.ProcessObserverCallback;
51 import android.car.builtin.display.DisplayManagerHelper;
52 import android.car.builtin.util.Slogf;
53 import android.car.occupantconnection.ICarRemoteDevice;
54 import android.car.occupantconnection.IStateCallback;
55 import android.car.user.CarUserManager;
56 import android.car.user.UserLifecycleEventFilter;
57 import android.content.BroadcastReceiver;
58 import android.content.Context;
59 import android.content.Intent;
60 import android.content.IntentFilter;
61 import android.content.pm.PackageInfo;
62 import android.content.pm.PackageManager;
63 import android.hardware.display.DisplayManager.DisplayListener;
64 import android.os.Binder;
65 import android.os.RemoteException;
66 import android.os.UserHandle;
67 import android.os.UserManager;
68 import android.util.ArrayMap;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.SparseArray;
72 import android.util.proto.ProtoOutputStream;
73 
74 import com.android.car.CarLocalServices;
75 import com.android.car.CarOccupantZoneService;
76 import com.android.car.CarServiceBase;
77 import com.android.car.SystemActivityMonitoringService;
78 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
79 import com.android.car.internal.util.BinderKeyValueContainer;
80 import com.android.car.internal.util.IndentingPrintWriter;
81 import com.android.car.power.CarPowerManagementService;
82 import com.android.car.user.CarUserService;
83 import com.android.internal.annotations.GuardedBy;
84 import com.android.internal.annotations.VisibleForTesting;
85 
86 import java.lang.annotation.Retention;
87 import java.lang.annotation.RetentionPolicy;
88 import java.util.Arrays;
89 import java.util.List;
90 import java.util.Set;
91 
92 /**
93  * Service to implement APIs defined in {@link android.car.CarRemoteDeviceManager}.
94  * <p>
95  * In this class, a discovering client refers to a client that has registered an {@link
96  * IStateCallback}, and discovered apps refer to the peer apps of the discovering client.
97  * <p>
98  * This class can monitor the states of occupant zones in the car and the peer clients in
99  * those occupant zones. There are 3 {@link OccupantZoneState}s:
100  * <ul>
101  *   <li> {@link android.car.CarRemoteDeviceManager#FLAG_OCCUPANT_ZONE_POWER_ON} is updated by the
102  *        DisplayListener.
103  *   <li> TODO(b/257117236): implement FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED.
104  *   <li> {@link android.car.CarRemoteDeviceManager#FLAG_OCCUPANT_ZONE_CONNECTION_READY} is updated
105  *        by the ICarOccupantZoneCallback.
106  * </ul>
107  * There are 5 {@link AppState}s:
108  * <ul>
109  *   <li> App install states ({@link android.car.CarRemoteDeviceManager#FLAG_CLIENT_INSTALLED},
110  *        {@link android.car.CarRemoteDeviceManager#FLAG_CLIENT_SAME_LONG_VERSION},
111  *        {@link android.car.CarRemoteDeviceManager#FLAG_CLIENT_SAME_SIGNATURE}) are updated by the
112  *        PackageChangeReceiver.
113  *   <li> App running states ({@link android.car.CarRemoteDeviceManager#FLAG_CLIENT_RUNNING},
114  *        {@link android.car.CarRemoteDeviceManager#FLAG_CLIENT_IN_FOREGROUND}) are updated by the
115  *        ProcessRunningStateCallback. Note: these states won't be updated for apps that share the
116  *        same user ID through the "sharedUserId" mechanism.
117  * </ul>
118  */
119 public class CarRemoteDeviceService extends ICarRemoteDevice.Stub implements
120         CarServiceBase {
121 
122     private static final String TAG = CarRemoteDeviceService.class.getSimpleName();
123     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
124     private static final String INDENTATION_2 = "  ";
125     private static final String INDENTATION_4 = "    ";
126 
127     private static final int PROCESS_NOT_RUNNING = 0;
128     private static final int PROCESS_RUNNING_IN_BACKGROUND = 1;
129     private static final int PROCESS_RUNNING_IN_FOREGROUND = 2;
130 
131     @IntDef(flag = false, prefix = {"PROCESS_"}, value = {
132             PROCESS_NOT_RUNNING,
133             PROCESS_RUNNING_IN_BACKGROUND,
134             PROCESS_RUNNING_IN_FOREGROUND
135     })
136     @Retention(RetentionPolicy.SOURCE)
137     @interface ProcessRunningState {
138     }
139 
140     @VisibleForTesting
141     @AppState
142     static final int INITIAL_APP_STATE = 0;
143     @VisibleForTesting
144     @OccupantZoneState
145     static final int INITIAL_OCCUPANT_ZONE_STATE = 0;
146 
147     private final Object mLock = new Object();
148     private final Context mContext;
149     private final CarOccupantZoneService mOccupantZoneService;
150     private final CarPowerManagementService mPowerManagementService;
151     private final SystemActivityMonitoringService mSystemActivityMonitoringService;
152     private final ActivityManager mActivityManager;
153     private final UserManager mUserManager;
154 
155     /** A map of discovering client to its callback. */
156     @GuardedBy("mLock")
157     private final BinderKeyValueContainer<ClientId, IStateCallback> mCallbackMap;
158 
159     /** A map of client app to its {@link AppState}. */
160     @GuardedBy("mLock")
161     private final ArrayMap<ClientId, Integer> mAppStateMap;
162 
163     /**
164      * A map of occupant zone to its {@link OccupantZoneState}. Its keys are all the occupant
165      * zones on this SoC and will never change after initialization.
166      */
167     @GuardedBy("mLock")
168     private final ArrayMap<OccupantZoneInfo, Integer> mOccupantZoneStateMap;
169 
170     /** A map of secondary user (non-system user) ID to PerUserInfo. */
171     @GuardedBy("mLock")
172     private final SparseArray<PerUserInfo> mPerUserInfoMap;
173 
174     private final ProcessObserverCallback mProcessObserver = new ProcessObserver();
175 
176     private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
177         Slogf.v(TAG, "onEvent(%s)", event);
178         handleUserChange();
179     };
180 
181     private final class PackageChangeReceiver extends BroadcastReceiver {
182 
183         /** The user ID that this receiver registered as. */
184         private final int mUserId;
185         /** The occupant zone that the user runs in. */
186         private final OccupantZoneInfo mOccupantZone;
187 
188         @VisibleForTesting
PackageChangeReceiver(int userId, OccupantZoneInfo occupantZone)189         PackageChangeReceiver(int userId, OccupantZoneInfo occupantZone) {
190             super();
191             this.mUserId = userId;
192             this.mOccupantZone = occupantZone;
193         }
194 
195         @Override
onReceive(Context context, Intent intent)196         public void onReceive(Context context, Intent intent) {
197             String packageName = intent.getData().getSchemeSpecificPart();
198             synchronized (mLock) {
199                 if (!isDiscoveringLocked(packageName)) {
200                     // There is no peer client discovering this app, so ignore its install/uninstall
201                     // event.
202                     if (DBG) {
203                         Slogf.v(TAG, "Ignore package change for %s as user %d because there is no "
204                                 + "peer client discovering this app", packageName, mUserId);
205                     }
206                     return;
207                 }
208                 ClientId clientId = new ClientId(mOccupantZone, mUserId, packageName);
209                 if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
210                     Slogf.v(TAG, "%s was installed", clientId);
211                     @AppState int newState = calculateAppStateLocked(clientId);
212                     setAppStateLocked(clientId, newState, /* callbackToNotify= */ null);
213                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
214                     Slogf.v(TAG, "%s was uninstalled", clientId);
215                     setAppStateLocked(clientId, INITIAL_APP_STATE, /* callbackToNotify= */ null);
216                 }
217             }
218         }
219     }
220 
221     private final class ProcessObserver extends ProcessObserverCallback {
222         @Override
onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)223         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
224             handleProcessRunningStateChange(uid, foregroundActivities
225                     ? PROCESS_RUNNING_IN_FOREGROUND
226                     : PROCESS_RUNNING_IN_BACKGROUND);
227         }
228 
229         @Override
onProcessDied(int pid, int uid)230         public void onProcessDied(int pid, int uid) {
231             handleProcessRunningStateChange(uid, PROCESS_NOT_RUNNING);
232         }
233     }
234 
235     /** Wrapper class for objects that are specific to a non-system user. */
236     @VisibleForTesting
237     static final class PerUserInfo {
238 
239         /** The occupant zone that the user runs in. */
240         public final OccupantZoneInfo zone;
241 
242         /** The Context of the user. Used to register and unregister the receiver. */
243         public final Context context;
244 
245         /** The PackageManager of the user. */
246         public final PackageManager pm;
247 
248         /** The PackageChangeReceiver. Used to listen to package install/uninstall events. */
249         public final BroadcastReceiver receiver;
250 
251         @VisibleForTesting
PerUserInfo(OccupantZoneInfo zone, Context context, PackageManager pm, BroadcastReceiver receiver)252         PerUserInfo(OccupantZoneInfo zone, Context context, PackageManager pm,
253                 BroadcastReceiver receiver) {
254             this.zone = zone;
255             this.context = context;
256             this.pm = pm;
257             this.receiver = receiver;
258         }
259     }
260 
CarRemoteDeviceService(Context context, CarOccupantZoneService occupantZoneService, CarPowerManagementService powerManagementService, SystemActivityMonitoringService systemActivityMonitoringService)261     public CarRemoteDeviceService(Context context,
262             CarOccupantZoneService occupantZoneService,
263             CarPowerManagementService powerManagementService,
264             SystemActivityMonitoringService systemActivityMonitoringService) {
265         this(context, occupantZoneService, powerManagementService, systemActivityMonitoringService,
266                 context.getSystemService(ActivityManager.class),
267                 context.getSystemService(UserManager.class),
268                 /* perUserInfoMap= */ new SparseArray<>(),
269                 /* callbackMap= */ new BinderKeyValueContainer<>(),
270                 /* appStateMap= */ new ArrayMap<>(),
271                 /* occupantZoneStateMap= */ new ArrayMap<>());
272     }
273 
274     @VisibleForTesting
CarRemoteDeviceService(Context context, CarOccupantZoneService occupantZoneService, CarPowerManagementService powerManagementService, SystemActivityMonitoringService systemActivityMonitoringService, ActivityManager activityManager, UserManager userManager, SparseArray<PerUserInfo> perUserInfoMap, BinderKeyValueContainer<ClientId, IStateCallback> callbackMap, ArrayMap<ClientId, Integer> appStateMap, ArrayMap<OccupantZoneInfo, Integer> occupantZoneStateMap)275     CarRemoteDeviceService(Context context,
276             CarOccupantZoneService occupantZoneService,
277             CarPowerManagementService powerManagementService,
278             SystemActivityMonitoringService systemActivityMonitoringService,
279             ActivityManager activityManager,
280             UserManager userManager,
281             SparseArray<PerUserInfo> perUserInfoMap,
282             BinderKeyValueContainer<ClientId, IStateCallback> callbackMap,
283             ArrayMap<ClientId, Integer> appStateMap,
284             ArrayMap<OccupantZoneInfo, Integer> occupantZoneStateMap) {
285         mContext = context;
286         mOccupantZoneService = occupantZoneService;
287         mPowerManagementService = powerManagementService;
288         mSystemActivityMonitoringService = systemActivityMonitoringService;
289         mActivityManager = activityManager;
290         mUserManager = userManager;
291         mPerUserInfoMap = perUserInfoMap;
292         mCallbackMap = callbackMap;
293         mAppStateMap = appStateMap;
294         mOccupantZoneStateMap = occupantZoneStateMap;
295     }
296 
297     @Override
init()298     public void init() {
299         initAllOccupantZones();
300         registerUserLifecycleListener();
301         initAssignedUsers();
302         registerDisplayListener();
303     }
304 
305     @Override
release()306     public void release() {
307         // TODO(b/257117236): implement this method.
308     }
309 
310     /** Run `adb shell dumpsys car_service --services CarRemoteDeviceService` to dump. */
311     @Override
312     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)313     public void dump(IndentingPrintWriter writer) {
314         writer.println("*CarRemoteDeviceService*");
315         synchronized (mLock) {
316             writer.printf("%smCallbackMap:\n", INDENTATION_2);
317             for (int i = 0; i < mCallbackMap.size(); i++) {
318                 ClientId discoveringClient = mCallbackMap.keyAt(i);
319                 IStateCallback callback = mCallbackMap.valueAt(i);
320                 writer.printf("%s%s, callback:%s\n", INDENTATION_4, discoveringClient, callback);
321             }
322             writer.printf("%smAppStateMap:\n", INDENTATION_2);
323             for (int i = 0; i < mAppStateMap.size(); i++) {
324                 ClientId client = mAppStateMap.keyAt(i);
325                 @AppState int state = mAppStateMap.valueAt(i);
326                 writer.printf("%s%s, state:%s\n", INDENTATION_4, client, appStateToString(state));
327             }
328             writer.printf("%smOccupantZoneStateMap:\n", INDENTATION_2);
329             for (int i = 0; i < mOccupantZoneStateMap.size(); i++) {
330                 OccupantZoneInfo occupantZone = mOccupantZoneStateMap.keyAt(i);
331                 @OccupantZoneState int state = mOccupantZoneStateMap.valueAt(i);
332                 writer.printf("%s%s, state:%s\n", INDENTATION_4, occupantZone,
333                         occupantZoneStateToString(state));
334             }
335             writer.printf("%smPerUserInfoMap:\n", INDENTATION_2);
336             for (int i = 0; i < mPerUserInfoMap.size(); i++) {
337                 int userId = mPerUserInfoMap.keyAt(i);
338                 PerUserInfo info = mPerUserInfoMap.valueAt(i);
339                 writer.printf("%suserId:%s, %s, %s, %s, %s\n", INDENTATION_4, userId, info.zone,
340                         info.context, info.pm, info.receiver);
341             }
342         }
343     }
344 
345     @Override
346     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)347     public void dumpProto(ProtoOutputStream proto) {}
348 
349     @Override
registerStateCallback(String packageName, IStateCallback callback)350     public void registerStateCallback(String packageName, IStateCallback callback) {
351         assertPermission(mContext, Car.PERMISSION_MANAGE_REMOTE_DEVICE);
352         checkCalledByPackage(mContext, packageName);
353 
354         ClientId discoveringClient = getCallingClientId(packageName);
355         synchronized (mLock) {
356             assertNoDuplicateCallbackLock(discoveringClient);
357             boolean firstDiscoverer = mCallbackMap.size() == 0;
358             mCallbackMap.put(discoveringClient, callback);
359             // Notify the discoverer of the latest states.
360             updateAllOccupantZoneStateLocked(callback);
361             updateAllAppStateWithPackageNameLocked(discoveringClient.packageName, callback);
362 
363             if (firstDiscoverer) {
364                 mSystemActivityMonitoringService.registerProcessObserverCallback(mProcessObserver);
365             }
366         }
367     }
368 
369     @Override
unregisterStateCallback(String packageName)370     public void unregisterStateCallback(String packageName) {
371         assertPermission(mContext, Car.PERMISSION_MANAGE_REMOTE_DEVICE);
372         checkCalledByPackage(mContext, packageName);
373 
374         ClientId discoveringClient = getCallingClientId(packageName);
375         synchronized (mLock) {
376             assertHasCallbackLock(discoveringClient);
377             mCallbackMap.remove(discoveringClient);
378             if (mCallbackMap.size() == 0) {
379                 mSystemActivityMonitoringService.unregisterProcessObserverCallback(
380                         mProcessObserver);
381             }
382             // If this discoverer is the last discoverer with the package name, remove the app state
383             // of all the apps with the package name.
384             if (!isDiscoveringLocked(packageName)) {
385                 clearAllAppStateWithPackageNameLocked(packageName);
386             }
387         }
388     }
389 
390     @Override
getEndpointPackageInfo(int occupantZoneId, String packageName)391     public PackageInfo getEndpointPackageInfo(int occupantZoneId, String packageName) {
392         assertPermission(mContext, Car.PERMISSION_MANAGE_REMOTE_DEVICE);
393         checkCalledByPackage(mContext, packageName);
394 
395         int userId = mOccupantZoneService.getUserForOccupant(occupantZoneId);
396         if (userId == INVALID_USER_ID) {
397             Slogf.e(TAG, "Failed to get PackageInfo of %s in occupant zone %d because it has no "
398                     + "user assigned", packageName, occupantZoneId);
399             return null;
400         }
401         return getPackageInfoAsUser(packageName, userId);
402     }
403 
404     @Override
setOccupantZonePower(OccupantZoneInfo occupantZone, boolean powerOn)405     public void setOccupantZonePower(OccupantZoneInfo occupantZone, boolean powerOn) {
406         assertPermission(mContext, Car.PERMISSION_MANAGE_REMOTE_DEVICE);
407 
408         int[] displayIds = mOccupantZoneService.getAllDisplaysForOccupantZone(occupantZone.zoneId);
409         for (int id : displayIds) {
410             mPowerManagementService.setDisplayPowerState(id, powerOn);
411         }
412     }
413 
414     @Override
isOccupantZonePowerOn(OccupantZoneInfo occupantZone)415     public boolean isOccupantZonePowerOn(OccupantZoneInfo occupantZone) {
416         assertPermission(mContext, Car.PERMISSION_MANAGE_REMOTE_DEVICE);
417 
418         return mOccupantZoneService.areDisplaysOnForOccupantZone(occupantZone.zoneId);
419     }
420 
initAllOccupantZones()421     private void initAllOccupantZones() {
422         List<OccupantZoneInfo> allOccupantZones = mOccupantZoneService.getAllOccupantZones();
423         synchronized (mLock) {
424             for (int i = 0; i < allOccupantZones.size(); i++) {
425                 OccupantZoneInfo occupantZone = allOccupantZones.get(i);
426                 @OccupantZoneState int initialState = calculateOccupantZoneState(occupantZone);
427                 Slogf.v(TAG, "The state of %s is initialized to %s", occupantZone,
428                         occupantZoneStateToString(initialState));
429                 mOccupantZoneStateMap.put(occupantZone, initialState);
430             }
431         }
432     }
433 
registerUserLifecycleListener()434     private void registerUserLifecycleListener() {
435         CarUserService userService = CarLocalServices.getService(CarUserService.class);
436         UserLifecycleEventFilter userEventFilter = new UserLifecycleEventFilter.Builder()
437                 // It listens to user STARTING event because it needs to initialize PerUserInfo for
438                 // the new user as early as possible (b/300676850).
439                 // It listens to user UNLOCKED and INVISIBLE events because it needs to update the
440                 // OccupantZoneState. UNLOCKED or VISIBLE event indicates the connection becomes
441                 // ready, while INVISIBLE event indicates the connection changes to not ready.
442                 // It listens to user SWITCHING event because it needs to update PerUserInfo through
443                 // CarOccupantZoneService (b/331780823).
444                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STARTING)
445                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
446                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_VISIBLE)
447                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_INVISIBLE)
448                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
449                 .build();
450         userService.addUserLifecycleListener(userEventFilter, mUserLifecycleListener);
451     }
452 
453     /**
454      * Handles the user change in all the occupant zones, including the driver occupant zone and
455      * passenger occupant zones.
456      */
handleUserChange()457     private void handleUserChange() {
458         synchronized (mLock) {
459             for (int i = 0; i < mOccupantZoneStateMap.size(); i++) {
460                 OccupantZoneInfo occupantZone = mOccupantZoneStateMap.keyAt(i);
461                 int oldUserId = getAssignedUserLocked(occupantZone);
462                 int newUserId =
463                         mOccupantZoneService.getUserForOccupant(occupantZone.zoneId);
464                 Slogf.i(TAG, "In %s, old user was %d, new user is %d",
465                         occupantZone, oldUserId, newUserId);
466                 boolean hasOldUser = (oldUserId != INVALID_USER_ID);
467                 boolean hasNewUser = isNonSystemUser(newUserId);
468 
469                 if (!hasOldUser && !hasNewUser) {
470                     // Still no user secondary assigned in this occupant zone, so do nothing.
471                     Slogf.v(TAG, "Still no user secondary assigned in %s", occupantZone);
472                     continue;
473                 }
474                 if (oldUserId == newUserId) {
475                     // The user ID doesn't change in this occupant zone, but the user lifecycle
476                     // might have changed, so try to update the occupant zone state.
477                     handleSameUserUpdateLocked(newUserId, occupantZone);
478                     continue;
479                 }
480                 if (hasOldUser && !hasNewUser) {
481                     // The old user was unassigned.
482                     handleUserUnassignedLocked(oldUserId, occupantZone);
483                     continue;
484                 }
485                 if (!hasOldUser && hasNewUser) {
486                     // The new user was assigned.
487                     handleUserAssignedLocked(newUserId, occupantZone);
488                     continue;
489                 }
490                 // The old user switched to a different new user.
491                 handleUserSwitchedLocked(oldUserId, newUserId, occupantZone);
492             }
493         }
494     }
495 
registerDisplayListener()496     private void registerDisplayListener() {
497         DisplayManagerHelper.registerDisplayListener(mContext, new DisplayListener() {
498             @Override
499             public void onDisplayAdded(int displayId) {
500                 // No-op.
501             }
502 
503             @Override
504             public void onDisplayRemoved(int displayId) {
505                 // No-op.
506             }
507 
508             @Override
509             public void onDisplayChanged(int displayId) {
510                 Slogf.v(TAG, "onDisplayChanged(): displayId %d", displayId);
511                 OccupantZoneInfo occupantZone =
512                         mOccupantZoneService.getOccupantZoneForDisplayId(displayId);
513                 if (occupantZone == null) {
514                     Slogf.i(TAG, "Display %d has no occupant zone assigned", displayId);
515                     return;
516                 }
517                 synchronized (mLock) {
518                     updateOccupantZoneStateLocked(occupantZone,
519                             /* callbackToNotify= */null);
520                 }
521             }
522         }, /* handler= */null, EVENT_FLAG_DISPLAY_CHANGED);
523     }
524 
handleProcessRunningStateChange(int uid, @ProcessRunningState int newState)525     private void handleProcessRunningStateChange(int uid, @ProcessRunningState int newState) {
526         UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
527         if (userHandle.isSystem()) {
528             if (DBG) {
529                 Slogf.v(TAG, "Skip ProcessRunningState change for process with uid %d because "
530                         + "the process runs as system user", uid);
531             }
532             return;
533         }
534         synchronized (mLock) {
535             String packageName = getUniquePackageNameByUidLocked(uid);
536             if (packageName == null) {
537                 return;
538             }
539             if (!isDiscoveringLocked(packageName)) {
540                 // There is no peer client discovering this app, so ignore its running state change
541                 // event.
542                 if (DBG) {
543                     Slogf.v(TAG, "Skip ProcessRunningState change for %s because there is no peer "
544                             + "client discovering this app", packageName);
545                 }
546                 return;
547             }
548             Slogf.v(TAG, "%s 's running state changed to %s", packageName,
549                     processRunningStateToString(newState));
550             int userId = userHandle.getIdentifier();
551             // Note: userInfo can't be null here, otherwise getUniquePackageNameByUidLocked() would
552             // return null, and it wouldn't get here.
553             PerUserInfo userInfo = mPerUserInfoMap.get(userId);
554             ClientId clientId = new ClientId(userInfo.zone, userId, packageName);
555             @AppState int newAppState =
556                     convertProcessRunningStateToAppStateLocked(packageName, userId, newState);
557             setAppStateLocked(clientId, newAppState, /* callbackToNotify= */ null);
558         }
559     }
560 
initAssignedUsers()561     private void initAssignedUsers() {
562         synchronized (mLock) {
563             for (int i = 0; i < mOccupantZoneStateMap.size(); i++) {
564                 OccupantZoneInfo occupantZone = mOccupantZoneStateMap.keyAt(i);
565                 int userId = mOccupantZoneService.getUserForOccupant(occupantZone.zoneId);
566                 Slogf.v(TAG, "User ID of %s is %d ", occupantZone, userId);
567                 if (!isNonSystemUser(userId)) {
568                     continue;
569                 }
570                 initAssignedUserLocked(userId, occupantZone);
571             }
572         }
573     }
574 
575     /**
576      * Initializes PerUserInfo for the given user, and registers a PackageChangeReceiver for the
577      * given user.
578      *
579      * @return {@code true} if the PerUserInfo was initialized successfully
580      */
581     @GuardedBy("mLock")
initAssignedUserLocked(int userId, OccupantZoneInfo occupantZone)582     private boolean initAssignedUserLocked(int userId, OccupantZoneInfo occupantZone) {
583         if (!isNonSystemUser(userId)) {
584             Slogf.w(TAG, "%s is assigned to user %d", occupantZone, userId);
585             return false;
586         }
587         PerUserInfo userInfo = mPerUserInfoMap.get(userId);
588         if (userInfo != null && userInfo.zone.equals(occupantZone)) {
589             Slogf.v(TAG, "Skip initializing PerUserInfo of user %d because it exists already",
590                     userId);
591             return true;
592         }
593 
594         // Init PackageChangeReceiver.
595         PackageChangeReceiver receiver = new PackageChangeReceiver(userId, occupantZone);
596         IntentFilter filter = new IntentFilter();
597         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
598         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
599         filter.addDataScheme("package");
600 
601         // Init user Context.
602         Context userContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
603         if (userContext == null) {
604             Slogf.e(TAG, "Failed to create Context as user %d", userId);
605             return false;
606         }
607         Slogf.v(TAG, "registerReceiver() as user %d", userId);
608 
609         // Register PackageChangeReceiver.
610         userContext.registerReceiver(receiver, filter);
611 
612         // Init PackageManager.
613         PackageManager pm = userContext.getPackageManager();
614         if (pm == null) {
615             Slogf.e(TAG, "Failed to create PackageManager as user %d", userId);
616             return false;
617         }
618 
619         userInfo = new PerUserInfo(occupantZone, userContext, pm, receiver);
620         mPerUserInfoMap.put(userId, userInfo);
621         return true;
622     }
623 
624     /**
625      * Removes PerUserInfo of the given user, and unregisters the PackageChangeReceiver for the
626      * given user. This method is called when the given {@code userId} is unassigned.
627      */
628     @GuardedBy("mLock")
removeUnassignedUserLocked(int userId)629     private void removeUnassignedUserLocked(int userId) {
630         PerUserInfo userInfo = mPerUserInfoMap.get(userId);
631         if (userInfo == null) {
632             Slogf.v(TAG, "Skip removing PerUserInfo of user %d because it doesn't exist", userId);
633             return;
634         }
635         Slogf.v(TAG, "unregisterReceiver() as user %d", userId);
636         userInfo.context.unregisterReceiver(userInfo.receiver);
637 
638         mPerUserInfoMap.remove(userId);
639     }
640 
641     @GuardedBy("mLock")
handleSameUserUpdateLocked(int userId, OccupantZoneInfo occupantZone)642     private void handleSameUserUpdateLocked(int userId, OccupantZoneInfo occupantZone) {
643         Slogf.v(TAG, "User %d lifecycle might have changed in %s", userId, occupantZone);
644         updateOccupantZoneStateLocked(occupantZone, /* callbackToNotify= */ null);
645     }
646 
647     @GuardedBy("mLock")
handleUserUnassignedLocked(int userId, OccupantZoneInfo occupantZone)648     private void handleUserUnassignedLocked(int userId, OccupantZoneInfo occupantZone) {
649         Slogf.v(TAG, "User %d was unassigned in %s", userId, occupantZone);
650         removeUnassignedUserLocked(userId);
651         updateOccupantZoneStateLocked(occupantZone, /* callbackToNotify= */ null);
652         clearAllAppStateAsUserLocked(userId);
653     }
654 
655     @GuardedBy("mLock")
handleUserAssignedLocked(int userId, OccupantZoneInfo occupantZone)656     private void handleUserAssignedLocked(int userId, OccupantZoneInfo occupantZone) {
657         Slogf.v(TAG, "User %d was assigned in %s", userId, occupantZone);
658         initAssignedUserLocked(userId, occupantZone);
659         updateOccupantZoneStateLocked(occupantZone, /* callbackToNotify= */ null);
660         updateAllAppStateForNewUserLocked(userId, occupantZone);
661     }
662 
663     @GuardedBy("mLock")
handleUserSwitchedLocked(int oldUserId, int newUserId, OccupantZoneInfo occupantZone)664     private void handleUserSwitchedLocked(int oldUserId, int newUserId,
665             OccupantZoneInfo occupantZone) {
666         Slogf.v(TAG, "User %d was switched to %d in %s", oldUserId, newUserId, occupantZone);
667         removeUnassignedUserLocked(oldUserId);
668         clearAllAppStateAsUserLocked(oldUserId);
669 
670         initAssignedUserLocked(newUserId, occupantZone);
671         updateAllAppStateForNewUserLocked(newUserId, occupantZone);
672 
673         updateOccupantZoneStateLocked(occupantZone, /* callbackToNotify= */ null);
674     }
675 
getCallingClientId(String packageName)676     private ClientId getCallingClientId(String packageName) {
677         UserHandle callingUserHandle = Binder.getCallingUserHandle();
678         int callingUserId = callingUserHandle.getIdentifier();
679         OccupantZoneInfo occupantZone =
680                 mOccupantZoneService.getOccupantZoneForUser(callingUserHandle);
681         // Note: the occupantZone is not null because the calling user must be a valid user.
682         return new ClientId(occupantZone, callingUserId, packageName);
683     }
684 
685     /**
686      * Updates the states of all the occupant zones, notifies the newly registered callback
687      * {@code callbackToNotify} of the latest state if it is not {@code null}, and notifies other
688      * callbacks of the latest state if the state has changed.
689      */
690     @GuardedBy("mLock")
updateAllOccupantZoneStateLocked(@ullable IStateCallback callbackToNotify)691     private void updateAllOccupantZoneStateLocked(@Nullable IStateCallback callbackToNotify) {
692         for (int i = 0; i < mOccupantZoneStateMap.size(); i++) {
693             OccupantZoneInfo occupantZone = mOccupantZoneStateMap.keyAt(i);
694             updateOccupantZoneStateLocked(occupantZone, callbackToNotify);
695         }
696     }
697 
698     /**
699      * Updates the state of the given occupant zone, notifies the newly registered callback
700      * {@code callbackToNotify} of the latest state if it is not {@code null}, and notifies other
701      * callbacks of the latest state if the state has changed.
702      */
703     @GuardedBy("mLock")
updateOccupantZoneStateLocked(OccupantZoneInfo occupantZone, @Nullable IStateCallback callbackToNotify)704     private void updateOccupantZoneStateLocked(OccupantZoneInfo occupantZone,
705             @Nullable IStateCallback callbackToNotify) {
706         @OccupantZoneState int oldState = mOccupantZoneStateMap.get(occupantZone);
707         @OccupantZoneState int newState = calculateOccupantZoneState(occupantZone);
708         boolean stateChanged = (oldState != newState);
709         if (!stateChanged && callbackToNotify == null) {
710             Slogf.v(TAG, "Skip updateOccupantZoneStateLocked() for %s because OccupantZoneState"
711                     + " stays the same and there is no newly registered callback", occupantZone);
712             return;
713         }
714         Slogf.v(TAG, "The state of %s is changed from %s to %s", occupantZone,
715                 occupantZoneStateToString(oldState), occupantZoneStateToString(newState));
716         mOccupantZoneStateMap.put(occupantZone, newState);
717 
718         for (int i = 0; i < mCallbackMap.size(); i++) {
719             ClientId discoveringClient = mCallbackMap.keyAt(i);
720             // Don't notify discovering clients that are running in this occupant zone.
721             if (discoveringClient.occupantZone.equals(occupantZone)) {
722                 continue;
723             }
724             IStateCallback callback = mCallbackMap.valueAt(i);
725             // If the callback is newly registered, invoke it anyway. Otherwise, invoke it only
726             // when the state has changed
727             if ((callback == callbackToNotify) || stateChanged) {
728                 try {
729                     callback.onOccupantZoneStateChanged(occupantZone, newState);
730                 } catch (RemoteException e) {
731                     Slogf.e(TAG, e, "Failed to notify %s of OccupantZoneState change",
732                             discoveringClient);
733                 }
734             }
735         }
736     }
737 
738     @VisibleForTesting
739     @OccupantZoneState
calculateOccupantZoneState(OccupantZoneInfo occupantZone)740     int calculateOccupantZoneState(OccupantZoneInfo occupantZone) {
741         @OccupantZoneState int occupantZoneState = INITIAL_OCCUPANT_ZONE_STATE;
742         // The three occupant zones states are independent of each other.
743         if (isPowerOn(occupantZone)) {
744             occupantZoneState |= FLAG_OCCUPANT_ZONE_POWER_ON;
745         }
746         if (isScreenUnlocked(occupantZone)) {
747             occupantZoneState |= FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED;
748         }
749         if (isConnectionReady(occupantZone)) {
750             occupantZoneState |= FLAG_OCCUPANT_ZONE_CONNECTION_READY;
751         }
752         return occupantZoneState;
753     }
754 
isPowerOn(OccupantZoneInfo occupantZone)755     private boolean isPowerOn(OccupantZoneInfo occupantZone) {
756         return mOccupantZoneService.areDisplaysOnForOccupantZone(occupantZone.zoneId);
757     }
758 
isScreenUnlocked(OccupantZoneInfo occupantZone)759     private boolean isScreenUnlocked(OccupantZoneInfo occupantZone) {
760         // TODO(b/257117236): implement this method.
761         return false;
762     }
763 
764     /**
765      * Returns {@code true} if the given {@code occupantZone} is ready to handle connection request.
766      * Returns {@code false} otherwise.
767      * <p>
768      * If the {@code occupantZone} is on the same SoC as the caller occupant zone, connection ready
769      * means the user is ready. If the {@code occupantZone} is on another SoC, connection ready
770      * means user ready and internet connection from the caller occupant zone to the {@code
771      * occupantZone} is good. User ready means the user has been allocated to the occupant zone,
772      * is actively running, is unlocked, and is visible.
773      */
774     // TODO(b/257118327): support multi-SoC.
isConnectionReady(OccupantZoneInfo occupantZone)775     boolean isConnectionReady(OccupantZoneInfo occupantZone) {
776         int userId = mOccupantZoneService.getUserForOccupant(occupantZone.zoneId);
777         if (!isNonSystemUser(userId)) {
778             return false;
779         }
780         UserHandle userHandle = UserHandle.of(userId);
781         return mUserManager.isUserRunning(userHandle) && mUserManager.isUserUnlocked(userHandle)
782                 && mUserManager.getVisibleUsers().contains(userHandle);
783     }
784 
785     /**
786      * Updates the {@link AppState} of all the apps with the given {@code packageName}, notifies
787      * the newly registered callback {@code callbackToNotify} of the latest state, and notifies
788      * other callbacks of the latest state if the state has changed.
789      */
790     @GuardedBy("mLock")
updateAllAppStateWithPackageNameLocked(String packageName, IStateCallback callbackToNotify)791     private void updateAllAppStateWithPackageNameLocked(String packageName,
792             IStateCallback callbackToNotify) {
793         for (int i = 0; i < mPerUserInfoMap.size(); i++) {
794             int userId = mPerUserInfoMap.keyAt(i);
795             OccupantZoneInfo occupantZone = mPerUserInfoMap.valueAt(i).zone;
796             ClientId discoveredClient = new ClientId(occupantZone, userId, packageName);
797             @AppState int newState = calculateAppStateLocked(discoveredClient);
798             setAppStateLocked(discoveredClient, newState, callbackToNotify);
799         }
800     }
801 
802     /**
803      * Updates the {@link AppState} of all the clients that run as {@code userId}, and notifies
804      * the discoverers of the state change. This method is invoked when a new user is assigned to
805      * the given occupant zone.
806      */
807     @GuardedBy("mLock")
updateAllAppStateForNewUserLocked(int userId, OccupantZoneInfo occupantZone)808     private void updateAllAppStateForNewUserLocked(int userId, OccupantZoneInfo occupantZone) {
809         Set<String> updatedApps = new ArraySet<>();
810         for (int i = 0; i < mCallbackMap.size(); i++) {
811             ClientId discoveringClient = mCallbackMap.keyAt(i);
812             // For a given package name, there might be several discoverers (peer clients that have
813             // registered a callback), but we only need to update the state of the changed client
814             // once.
815             if (updatedApps.contains(discoveringClient.packageName)) {
816                 continue;
817             }
818             updatedApps.add(discoveringClient.packageName);
819 
820             ClientId clientId = new ClientId(occupantZone, userId, discoveringClient.packageName);
821             @AppState int newAppState = calculateAppStateLocked(clientId);
822             setAppStateLocked(clientId, newAppState, /* callbackToNotify= */ null);
823         }
824     }
825 
826     /**
827      * Clears the {@link AppState} of all the apps that run as {@code userId}.
828      * This method is called when the given {@code userId} is unassigned for the occupantZone,
829      * for which the discoverers are already notified, so there is no need to notify the discoverers
830      * in this method.
831      */
832     @GuardedBy("mLock")
clearAllAppStateAsUserLocked(int userId)833     private void clearAllAppStateAsUserLocked(int userId) {
834         for (int i = 0; i < mAppStateMap.size(); i++) {
835             ClientId clientId = mAppStateMap.keyAt(i);
836             if (clientId.userId == userId) {
837                 mAppStateMap.removeAt(i);
838             }
839         }
840     }
841 
842     /**
843      * Clears the {@link AppState} of all the apps that have the given {@code packageName}.
844      * This method is called when the last discoverer with the package name is unregistered , so
845      * there is no need to notify the discoverers in this method.
846      */
847     @GuardedBy("mLock")
clearAllAppStateWithPackageNameLocked(String packageName)848     private void clearAllAppStateWithPackageNameLocked(String packageName) {
849         for (int i = 0; i < mAppStateMap.size(); i++) {
850             ClientId clientId = mAppStateMap.keyAt(i);
851             if (clientId.packageName.equals(packageName)) {
852                 mAppStateMap.removeAt(i);
853             }
854         }
855     }
856 
857     @GuardedBy("mLock")
getPackageManagerAsUserLocked(int userId)858     private PackageManager getPackageManagerAsUserLocked(int userId) {
859         PerUserInfo userInfo = mPerUserInfoMap.get(userId);
860         if (userInfo == null) {
861             Slogf.e(TAG, "Failed to get PackageManager as user %d because the user is not"
862                     + " assigned to an occupant zone yet", userId);
863             return null;
864         }
865         return userInfo.pm;
866     }
867 
868     @GuardedBy("mLock")
assertNoDuplicateCallbackLock(ClientId discoveredClient)869     private void assertNoDuplicateCallbackLock(ClientId discoveredClient) {
870         if (mCallbackMap.containsKey(discoveredClient)) {
871             throw new IllegalStateException("The client already registered a StateCallback: "
872                     + discoveredClient);
873         }
874     }
875 
876     @GuardedBy("mLock")
assertHasCallbackLock(ClientId discoveredClient)877     private void assertHasCallbackLock(ClientId discoveredClient) {
878         if (!mCallbackMap.containsKey(discoveredClient)) {
879             throw new IllegalStateException("The client has no StateCallback registered: "
880                     + discoveredClient);
881         }
882     }
883 
884     /**
885      * Returns {@code true} if there is a client with the {@code packageName} has registered an
886      * {@link IStateCallback}.
887      */
888     @GuardedBy("mLock")
isDiscoveringLocked(String packageName)889     private boolean isDiscoveringLocked(String packageName) {
890         for (int i = 0; i < mCallbackMap.size(); i++) {
891             ClientId discoveringClient = mCallbackMap.keyAt(i);
892             if (discoveringClient.packageName.equals(packageName)) {
893                 return true;
894             }
895         }
896         return false;
897     }
898 
899     /**
900      * Sets the {@link AppState} of the given client, notifies the newly registered callback
901      * {@code callbackToNotify} of the latest state if it is not {@code null}, and notifies other
902      * peer discoverers of the latest state if the state has changed.
903      */
904     @GuardedBy("mLock")
setAppStateLocked(ClientId discoveredClient, @AppState int newState, @Nullable IStateCallback callbackToNotify)905     private void setAppStateLocked(ClientId discoveredClient, @AppState int newState,
906             @Nullable IStateCallback callbackToNotify) {
907         Integer oldAppState = mAppStateMap.get(discoveredClient);
908         boolean stateChanged = (oldAppState == null || oldAppState.intValue() != newState);
909         if (!stateChanged && callbackToNotify == null) {
910             Slogf.v(TAG, "Skip setAppStateLocked() because AppState stays the same and there"
911                     + " is no newly registered callback");
912             return;
913         }
914         Slogf.v(TAG, "The app state of %s is set from %s to %s", discoveredClient,
915                 oldAppState == null ? "null" : appStateToString(oldAppState),
916                 appStateToString(newState));
917         mAppStateMap.put(discoveredClient, newState);
918 
919         // Notify its peer clients that are discovering.
920         for (int i = 0; i < mCallbackMap.size(); i++) {
921             ClientId discoveringClient = mCallbackMap.keyAt(i);
922             // A peer client is a client that has the same package name but runs as another user.
923             // If it is not a peer client, skip it.
924             if (!discoveringClient.packageName.equals(discoveredClient.packageName)
925                     || discoveringClient.userId == discoveredClient.userId) {
926                 continue;
927             }
928             IStateCallback callback = mCallbackMap.valueAt(i);
929             // If the callback is newly registered, invoke it anyway. Otherwise, invoke it only
930             // when the state has changed
931             if (callback == callbackToNotify || stateChanged) {
932                 try {
933                     callback.onAppStateChanged(discoveredClient.occupantZone, newState);
934                 } catch (RemoteException e) {
935                     Slogf.e(TAG, e, "Failed to notify %d of AppState change", discoveringClient);
936                 }
937             }
938         }
939     }
940 
941     @GuardedBy("mLock")
942     @AppState
calculateAppStateLocked(ClientId clientId)943     private int calculateAppStateLocked(ClientId clientId) {
944         @AppState int appState = INITIAL_APP_STATE;
945         if (isAppInstalledAsUserLocked(clientId.packageName, clientId.userId)) {
946             appState |= FLAG_CLIENT_INSTALLED;
947             // In single-SoC model, the peer client is guaranteed to have the same
948             // signing info and long version code.
949             // TODO(b/257118327): support multiple-SoC.
950             appState |= FLAG_CLIENT_SAME_LONG_VERSION | FLAG_CLIENT_SAME_SIGNATURE;
951 
952             RunningAppProcessInfo info =
953                     getRunningAppProcessInfoAsUserLocked(clientId.packageName, clientId.userId);
954             if (isAppRunning(info)) {
955                 appState |= FLAG_CLIENT_RUNNING;
956                 if (isAppRunningInForeground(info)) {
957                     appState |= FLAG_CLIENT_IN_FOREGROUND;
958                 }
959             }
960         }
961         return appState;
962     }
963 
964     @GuardedBy("mLock")
getAssignedUserLocked(OccupantZoneInfo occupantZone)965     private int getAssignedUserLocked(OccupantZoneInfo occupantZone) {
966         for (int i = 0; i < mPerUserInfoMap.size(); i++) {
967             if (occupantZone.equals(mPerUserInfoMap.valueAt(i).zone)) {
968                 return mPerUserInfoMap.keyAt(i);
969             }
970         }
971         return INVALID_USER_ID;
972     }
973 
974     /**
975      * This method is an unlocked version of {@link #calculateAppStateLocked} and is used for
976      * testing only.
977      */
978     @AppState
979     @VisibleForTesting
calculateAppState(ClientId clientId)980     int calculateAppState(ClientId clientId) {
981         synchronized (mLock) {
982             return calculateAppStateLocked(clientId);
983         }
984     }
985 
986     @GuardedBy("mLock")
isAppInstalledAsUserLocked(String packageName, int userId)987     private boolean isAppInstalledAsUserLocked(String packageName, int userId) {
988         return getPackageInfoAsUserLocked(packageName, userId, /* flags= */ 0) != null;
989     }
990 
getPackageInfoAsUser(String packageName, int userId)991     PackageInfo getPackageInfoAsUser(String packageName, int userId) {
992         synchronized (mLock) {
993             return getPackageInfoAsUserLocked(packageName, userId, GET_SIGNING_CERTIFICATES);
994         }
995     }
996 
997     @GuardedBy("mLock")
getPackageInfoAsUserLocked(String packageName, int userId, int flags)998     private PackageInfo getPackageInfoAsUserLocked(String packageName, int userId, int flags) {
999         PackageManager pm = getPackageManagerAsUserLocked(userId);
1000         if (pm == null) {
1001             return null;
1002         }
1003         try {
1004             return pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags));
1005         } catch (PackageManager.NameNotFoundException e) {
1006             return null;
1007         }
1008     }
1009 
1010     @GuardedBy("mLock")
getRunningAppProcessInfoAsUserLocked(String packageName, int userId)1011     private RunningAppProcessInfo getRunningAppProcessInfoAsUserLocked(String packageName,
1012             int userId) {
1013         List<RunningAppProcessInfo> infos = mActivityManager.getRunningAppProcesses();
1014         if (infos == null) {
1015             return null;
1016         }
1017         for (int i = 0; i < infos.size(); i++) {
1018             RunningAppProcessInfo processInfo = infos.get(i);
1019             if (processInfo.processName.equals(packageName)) {
1020                 UserHandle processUserHandle = UserHandle.getUserHandleForUid(processInfo.uid);
1021                 if (processUserHandle.getIdentifier() == userId) {
1022                     return processInfo;
1023                 }
1024             }
1025         }
1026         return null;
1027     }
1028 
1029     @GuardedBy("mLock")
getUniquePackageNameByUidLocked(int uid)1030     private String getUniquePackageNameByUidLocked(int uid) {
1031         UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
1032         int userId = userHandle.getIdentifier();
1033         PerUserInfo userInfo = mPerUserInfoMap.get(userId);
1034         if (userInfo == null) {
1035             // When an occupant zone is assigned with a user, the associated PerUserInfo will be
1036             // initialized in the ICarOccupantZoneCallback. But the ICarOccupantZoneCallback may be
1037             // invoked after this method (called by ProcessObserverCallback). In that case, the
1038             // PerUserInfo will be null (b/277956688). So let's try to initialize the PerUserInfo
1039             // here.
1040             Slogf.v(TAG, "PerUserIno for user %d is not initialized yet", userId);
1041             OccupantZoneInfo occupantZone = mOccupantZoneService.getOccupantZoneForUser(userHandle);
1042             if (occupantZone == null) {
1043                 // This shouldn't happen. Let's log an error.
1044                 Slogf.e(TAG, "The running state of the process (uid %d) has changed, but the user"
1045                         + " %d is not assigned to any occupant zone yet", uid, userId);
1046                 return null;
1047             }
1048             boolean success = initAssignedUserLocked(userId, occupantZone);
1049             if (!success) {
1050                 Slogf.wtf(TAG, "Failed to initialize PerUserInfo for user %d in %s", userId,
1051                         occupantZone);
1052                 return null;
1053             }
1054             // Note: userInfo must not be null here because it was initialized successfully.
1055             userInfo = mPerUserInfoMap.get(userId);
1056         }
1057         String[] packageNames = userInfo.pm.getPackagesForUid(uid);
1058         if (packageNames == null) {
1059             return null;
1060         }
1061         if (packageNames.length == 1) {
1062             return packageNames[0];
1063         }
1064         // packageNames.length can't be 0.
1065         // Multiple package names means multiple apps share the same user ID through the
1066         // "sharedUserId" mechanism. However, "sharedUserId" mechanism is deprecated in
1067         // API level 29, so let's log an error.
1068         Slogf.i(TAG, "Failed to get the package name by uid! Apps shouldn't use sharedUserId"
1069                 + " because it's deprecated in API level 29: %s", Arrays.toString(packageNames));
1070         return null;
1071     }
1072 
1073     @GuardedBy("mLock")
1074     @AppState
convertProcessRunningStateToAppStateLocked(String packageName, int userId, @ProcessRunningState int state)1075     private int convertProcessRunningStateToAppStateLocked(String packageName, int userId,
1076             @ProcessRunningState int state) {
1077         // Note: In single-SoC model, the peer client is guaranteed to have the same
1078         // signing info and long version code.
1079         // TODO(b/257118327): support multiple-SoC.
1080         switch (state) {
1081             case PROCESS_RUNNING_IN_BACKGROUND:
1082                 return FLAG_CLIENT_INSTALLED | FLAG_CLIENT_SAME_LONG_VERSION
1083                         | FLAG_CLIENT_SAME_SIGNATURE | FLAG_CLIENT_RUNNING;
1084             case PROCESS_RUNNING_IN_FOREGROUND:
1085                 return FLAG_CLIENT_INSTALLED | FLAG_CLIENT_SAME_LONG_VERSION
1086                         | FLAG_CLIENT_SAME_SIGNATURE | FLAG_CLIENT_RUNNING
1087                         | FLAG_CLIENT_IN_FOREGROUND;
1088             case PROCESS_NOT_RUNNING:
1089                 return isAppInstalledAsUserLocked(packageName, userId)
1090                         ? FLAG_CLIENT_INSTALLED | FLAG_CLIENT_SAME_LONG_VERSION
1091                         | FLAG_CLIENT_SAME_SIGNATURE
1092                         : INITIAL_APP_STATE;
1093 
1094         }
1095         throw new IllegalArgumentException("Undefined ProcessRunningState: " + state);
1096     }
1097 
isAppRunning(RunningAppProcessInfo info)1098     private static boolean isAppRunning(RunningAppProcessInfo info) {
1099         return info != null;
1100     }
1101 
isAppRunningInForeground(RunningAppProcessInfo info)1102     private static boolean isAppRunningInForeground(RunningAppProcessInfo info) {
1103         return info != null && info.importance == IMPORTANCE_FOREGROUND;
1104     }
1105 
1106     /** Returns {@code true} if the given user is a valid user and is not the system user. */
isNonSystemUser(int userId)1107     private static boolean isNonSystemUser(int userId) {
1108         return userId != INVALID_USER_ID && !UserHandle.of(userId).isSystem();
1109     }
1110 
1111     @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE)
occupantZoneStateToString(@ccupantZoneState int state)1112     private static String occupantZoneStateToString(@OccupantZoneState int state) {
1113         boolean powerOn = (state & FLAG_OCCUPANT_ZONE_POWER_ON) != 0;
1114         boolean screenUnlocked = (state & FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED) != 0;
1115         boolean connectionReady = (state & FLAG_OCCUPANT_ZONE_CONNECTION_READY) != 0;
1116         return new StringBuilder(64)
1117                 .append("[")
1118                 .append(powerOn ? "on, " : "off, ")
1119                 .append(screenUnlocked ? "unlocked, " : "locked, ")
1120                 .append(connectionReady ? "ready" : "not-ready")
1121                 .append("]")
1122                 .toString();
1123 
1124     }
1125 
1126     @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE)
appStateToString(@ppState int state)1127     private static String appStateToString(@AppState int state) {
1128         boolean installed = (state & FLAG_CLIENT_INSTALLED) != 0;
1129         boolean sameVersion = (state & FLAG_CLIENT_SAME_LONG_VERSION) != 0;
1130         boolean sameSignature = (state & FLAG_CLIENT_SAME_SIGNATURE) != 0;
1131         boolean running = (state & FLAG_CLIENT_RUNNING) != 0;
1132         boolean inForeground = (state & FLAG_CLIENT_IN_FOREGROUND) != 0;
1133         return new StringBuilder(64)
1134                 .append("[")
1135                 .append(installed ? "installed, " : "not-installed, ")
1136                 .append(sameVersion ? "same-version, " : "different-version, ")
1137                 .append(sameSignature ? "same-signature, " : "different-signature, ")
1138                 .append(!running
1139                         ? "not-running"
1140                         : (inForeground ? "foreground" : "background"))
1141                 .append("]")
1142                 .toString();
1143     }
1144 
1145     @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE)
processRunningStateToString(@rocessRunningState int state)1146     private static String processRunningStateToString(@ProcessRunningState int state) {
1147         switch (state) {
1148             case PROCESS_NOT_RUNNING:
1149                 return "not-running";
1150             case PROCESS_RUNNING_IN_BACKGROUND:
1151                 return "background";
1152             case PROCESS_RUNNING_IN_FOREGROUND:
1153                 return "foreground";
1154             default:
1155                 throw new IllegalArgumentException("Undefined ProcessRunningState: " + state);
1156         }
1157     }
1158 }
1159