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