• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.pm;
18 
19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE;
20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED;
21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_VISIBLE;
24 import static android.content.Context.BIND_AUTO_CREATE;
25 import static android.os.Process.INVALID_UID;
26 
27 import static com.android.car.CarLog.TAG_AM;
28 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
29 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU;
30 
31 import android.annotation.Nullable;
32 import android.annotation.SuppressLint;
33 import android.annotation.UserIdInt;
34 import android.app.ActivityManager;
35 import android.car.builtin.util.Slogf;
36 import android.car.hardware.power.CarPowerManager;
37 import android.car.hardware.power.ICarPowerStateListener;
38 import android.car.user.CarUserManager.UserLifecycleEvent;
39 import android.car.user.CarUserManager.UserLifecycleListener;
40 import android.car.user.UserLifecycleEventFilter;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.ServiceConnection;
47 import android.content.res.Resources;
48 import android.net.Uri;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.Looper;
52 import android.os.Message;
53 import android.os.UserHandle;
54 import android.os.UserManager;
55 import android.text.TextUtils;
56 import android.util.Log;
57 
58 import com.android.car.CarLocalServices;
59 import com.android.car.CarLog;
60 import com.android.car.R;
61 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
62 import com.android.car.internal.util.IndentingPrintWriter;
63 import com.android.car.power.CarPowerManagementService;
64 import com.android.car.user.CarUserService;
65 import com.android.internal.annotations.VisibleForTesting;
66 
67 import java.io.PrintWriter;
68 import java.util.ArrayList;
69 import java.util.Iterator;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Objects;
73 import java.util.Set;
74 import java.util.concurrent.ConcurrentHashMap;
75 import java.util.concurrent.Executor;
76 
77 /**
78  * Class that responsible for controlling vendor services that was opted in to be bound/started
79  * by the Car Service.
80  *
81  * <p>Thread-safety note: all code runs in the {@code Handler} provided in the constructor, whenever
82  * possible pass {@link #mHandler} when subscribe for callbacks otherwise redirect code to the
83  * handler.
84  */
85 final class VendorServiceController implements UserLifecycleListener {
86 
87     @VisibleForTesting
88     static final String TAG = CarLog.tagFor(VendorServiceController.class);
89 
90     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
91     private static final String PACKAGE_DATA_SCHEME = "package";
92 
93     private final List<VendorServiceInfo> mVendorServiceInfos = new ArrayList<>();
94     private final Map<ConnectionKey, VendorServiceConnection> mConnections =
95             new ConcurrentHashMap<>();
96     private final Context mContext;
97     private final UserManager mUserManager;
98     private final Handler mHandler;
99     private CarUserService mCarUserService;
100     private CarPowerManagementService mPowerManagementService;
101 
102     private final BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
103         @Override
104         public void onReceive(Context context, Intent intent) {
105             String action = intent.getAction();
106             if (DBG) {
107                 Slogf.d(TAG_AM, "Package change received with action = %s", action);
108             }
109 
110             Uri packageData = intent.getData();
111             if (packageData == null) {
112                 Slogf.wtf(TAG_AM, "null packageData");
113                 return;
114             }
115             String packageName = packageData.getSchemeSpecificPart();
116             if (packageName == null) {
117                 Slogf.w(TAG_AM, "null packageName");
118                 return;
119             }
120             int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
121             int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
122 
123             switch (action) {
124                 case Intent.ACTION_PACKAGE_CHANGED:
125                     // Fall through
126                 case Intent.ACTION_PACKAGE_REPLACED:
127                     // Fall through
128                 case Intent.ACTION_PACKAGE_ADDED:
129                     startOrBindServiceForPackage(packageName, userId);
130                     break;
131                 case Intent.ACTION_PACKAGE_REMOVED:
132                     stopOrUnbindService(packageName, userId);
133                     break;
134                 default:
135                     Slogf.w(TAG_AM, "This package change event (%s) can't be handled.",
136                             action);
137             }
138         }
139     };
140 
141     private final ICarPowerStateListener mCarPowerStateListener =
142             new ICarPowerStateListener.Stub() {
143                 @Override
144                 public void onStateChanged(int state, long expirationTimeMs) {
145                     if (DBG) {
146                         Slogf.d(TAG, "Power state change received. State = %d", state);
147                     }
148                     if (state == CarPowerManager.STATE_HIBERNATION_EXIT
149                             || state == CarPowerManager.STATE_SUSPEND_EXIT) {
150                         onPowerResumed();
151                     }
152                 }
153             };
154 
VendorServiceController(Context context, Looper looper)155     VendorServiceController(Context context, Looper looper) {
156         mContext = context;
157         mUserManager = context.getSystemService(UserManager.class);
158         mHandler = new Handler(looper);
159     }
160 
init()161     void init() {
162         if (!loadXmlConfiguration()) {
163             return;  // Nothing to do
164         }
165 
166         mPowerManagementService = CarLocalServices.getService(CarPowerManagementService.class);
167         mCarUserService = CarLocalServices.getService(CarUserService.class);
168         UserLifecycleEventFilter userLifecycleEventFilter =
169                 new UserLifecycleEventFilter.Builder()
170                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
171                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
172                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_VISIBLE)
173                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_INVISIBLE)
174                         .addEventType(USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED).build();
175         mCarUserService.addUserLifecycleListener(userLifecycleEventFilter, this);
176 
177         startOrBindServicesIfNeeded();
178         mPowerManagementService.registerListener(mCarPowerStateListener);
179         registerPackageChangeReceiver();
180     }
181 
release()182     void release() {
183         if (mVendorServiceInfos.isEmpty()) {
184             Slogf.d(TAG_AM, "Releasing VendorServiceController without deep cleaning as no vendor "
185                     + "service info present. ");
186             return;
187         }
188         if (mCarUserService != null) {
189             mCarUserService.removeUserLifecycleListener(this);
190         }
191         unregisterPackageChangeReceiver();
192         mPowerManagementService.unregisterListener(mCarPowerStateListener);
193         for (ConnectionKey key : mConnections.keySet()) {
194             stopOrUnbindService(key.mVendorServiceInfo, key.mUserHandle);
195         }
196         mVendorServiceInfos.clear();
197         mConnections.clear();
198     }
199 
200     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)201     public void dump(IndentingPrintWriter writer) {
202         writer.println("VendorServiceController:");
203 
204         writer.increaseIndent();
205         writer.printf("DBG=%b\n", DBG);
206 
207         writer.println("VendorServiceInfo:");
208         writer.increaseIndent();
209         for (VendorServiceInfo info : mVendorServiceInfos) {
210             writer.println(info.toString());
211         }
212         writer.decreaseIndent(); // end of VendorServiceInfo:
213 
214         writer.println("Connections:");
215         writer.increaseIndent();
216         for (VendorServiceConnection connection : mConnections.values()) {
217             connection.dump(writer);
218         }
219         writer.decreaseIndent(); // end of Connections:
220 
221         writer.decreaseIndent(); // end of VendorServiceController:
222     }
223 
224     @Override
onEvent(UserLifecycleEvent event)225     public void onEvent(UserLifecycleEvent event) {
226         if (DBG) {
227             Slogf.d(TAG, "onEvent(" + event + ")");
228         }
229         int userId = event.getUserId();
230         switch (event.getEventType()) {
231             case USER_LIFECYCLE_EVENT_TYPE_VISIBLE:
232                 mHandler.post(() -> handleOnUserVisible(userId));
233                 break;
234             case USER_LIFECYCLE_EVENT_TYPE_INVISIBLE:
235                 mHandler.post(() -> handleOnUserInvisible(userId));
236                 break;
237             case USER_LIFECYCLE_EVENT_TYPE_SWITCHING:
238                 mHandler.post(() -> handleOnUserSwitching(userId));
239                 break;
240             case USER_LIFECYCLE_EVENT_TYPE_UNLOCKED:
241                 mHandler.post(() -> handleOnUserUnlocked(userId, /* forPostUnlock= */ false));
242                 break;
243             case USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED:
244                 mHandler.post(() -> handleOnUserUnlocked(userId, /* forPostUnlock= */ true));
245                 break;
246             default:
247                 // Shouldn't happen as listener was registered with filter
248                 Slogf.wtf(TAG, "Invalid event: %s", event);
249         }
250     }
251 
252     /** Handles power resume events, starting services with `trigger=resume`. */
onPowerResumed()253     private void onPowerResumed() {
254         if (DBG) {
255             Slogf.d(TAG, "onPowerResumed()");
256         }
257 
258         int size = mVendorServiceInfos.size();
259         for (int i = 0; i < size; i++) {
260             VendorServiceInfo serviceInfo = mVendorServiceInfos.get(i);
261             // RESUME events handle the system user only. Current or visible users are handled by
262             // user lifecycle events (unlock, visible, etc).
263             boolean isForSystemOrAllUsers = serviceInfo.isSystemUserService();
264             boolean isResumeTrigger = serviceInfo.shouldStartOnResume();
265             if (isForSystemOrAllUsers && isResumeTrigger) {
266                 startOrBindService(serviceInfo, UserHandle.SYSTEM);
267             }
268         }
269     }
270 
registerPackageChangeReceiver()271     private void registerPackageChangeReceiver() {
272         IntentFilter filter = new IntentFilter();
273         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
274         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
275         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
276         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
277         filter.addDataScheme(PACKAGE_DATA_SCHEME);
278         mContext.registerReceiverForAllUsers(mPackageChangeReceiver, filter,
279                 /* broadcastPermission= */ null, /* scheduler= */ null,
280                 Context.RECEIVER_NOT_EXPORTED);
281     }
282 
unregisterPackageChangeReceiver()283     private void unregisterPackageChangeReceiver() {
284         mContext.unregisterReceiver(mPackageChangeReceiver);
285     }
286 
startOrBindServiceForPackage(String packageName, @UserIdInt int userId)287     private void startOrBindServiceForPackage(String packageName, @UserIdInt int userId) {
288         if (DBG) {
289             Slogf.d(TAG, "startOrBindServiceForPackage() for package=%s, userId=%d",
290                     packageName, userId);
291         }
292 
293         int currentUserId = ActivityManager.getCurrentUser();
294         int size = mVendorServiceInfos.size();
295         for (int i = 0; i < size; i++) {
296             VendorServiceInfo serviceInfo = mVendorServiceInfos.get(i);
297             // Start or bind the service when the package name matches and the user is in scope.
298             if (packageName.equals(serviceInfo.getIntent().getComponent().getPackageName())
299                     && isUserInScope(userId, serviceInfo, mCarUserService, currentUserId)) {
300                 startOrBindService(serviceInfo, UserHandle.of(userId));
301             }
302         }
303     }
304 
305     /** Checks if the given {@code serviceInfo} satisfies the user scope. */
isUserInScope(@serIdInt int userId, VendorServiceInfo serviceInfo, CarUserService carUserService, @UserIdInt int currentUserId)306     private static boolean isUserInScope(@UserIdInt int userId, VendorServiceInfo serviceInfo,
307             CarUserService carUserService, @UserIdInt int currentUserId) {
308         boolean isSystemUser = userId == UserHandle.SYSTEM.getIdentifier();
309         boolean isCurrentUser = userId == currentUserId;
310 
311         return (isSystemUser && serviceInfo.isSystemUserService())
312             || (isCurrentUser && serviceInfo.isForegroundUserService())
313             || ((serviceInfo.isVisibleUserService()
314                     || (!isCurrentUser && serviceInfo.isBackgroundVisibleUserService()))
315                 && carUserService.isUserVisible(userId));
316     }
317 
handleOnUserSwitching(@serIdInt int userId)318     private void handleOnUserSwitching(@UserIdInt int userId) {
319         // The user switch notification is obsolete if userId is different from the current
320         // foreground user. Ignore it.
321         int currentUserId = ActivityManager.getCurrentUser();
322         if (currentUserId != userId) {
323             Slogf.w(TAG, "Received userSwitch event for user " + userId
324                     + " while current foreground user is " + currentUserId + "."
325                     + " Ignore the switch user event.");
326             return;
327         }
328 
329         // Clean up the services which do not satisfy their configured user scope.
330         for (VendorServiceConnection connection : mConnections.values()) {
331             int connectedUserId = connection.mUser.getIdentifier();
332             if (!isUserInScope(connectedUserId, connection.mVendorServiceInfo, mCarUserService,
333                     currentUserId)) {
334                 connection.stopOrUnbindService();
335             }
336         }
337 
338         if (userId != UserHandle.SYSTEM.getIdentifier()) {
339             startOrBindServicesForUser(UserHandle.of(userId), /* forPostUnlock= */ null);
340         } else {
341             Slogf.wtf(TAG, "Unexpected to receive switch user event for system user");
342         }
343     }
344 
handleOnUserInvisible(@serIdInt int userId)345     private void handleOnUserInvisible(@UserIdInt int userId) {
346         if (DBG) {
347             Slogf.d(TAG, "handleOnUserInvisible(): user=%d", userId);
348         }
349 
350         for (VendorServiceConnection connection : mConnections.values()) {
351             VendorServiceInfo serviceInfo = connection.mVendorServiceInfo;
352             if (connection.isUser(userId)
353                     && (serviceInfo.isVisibleUserService()
354                             || serviceInfo.isBackgroundVisibleUserService())
355                     && !serviceInfo.isAllUserService()) {
356                 connection.stopOrUnbindService();
357             }
358         }
359     }
360 
handleOnUserVisible(@serIdInt int userId)361     private void handleOnUserVisible(@UserIdInt int userId) {
362         if (DBG) {
363             Slogf.d(TAG, "handleOnUserVisible(): user=%d", userId);
364         }
365 
366         startOrBindServicesForUser(UserHandle.of(userId), /* forPostUnlock= */ null);
367     }
368 
handleOnUserUnlocked(@serIdInt int userId, boolean forPostUnlock)369     private void handleOnUserUnlocked(@UserIdInt int userId, boolean forPostUnlock) {
370         if (DBG) {
371             Slogf.d(TAG, "handleOnUserUnlocked(): user=%d", userId);
372         }
373 
374         startOrBindServicesForUser(UserHandle.of(userId), forPostUnlock);
375     }
376 
startOrBindServicesForUser(UserHandle user, @Nullable Boolean forPostUnlock)377     private void startOrBindServicesForUser(UserHandle user, @Nullable Boolean forPostUnlock) {
378         boolean unlocked = mUserManager.isUserUnlockingOrUnlocked(user);
379         int currentUserId = ActivityManager.getCurrentUser();
380         int userId = user.getIdentifier();
381         for (VendorServiceInfo service: mVendorServiceInfos) {
382             if (forPostUnlock != null
383                     && service.shouldStartOnPostUnlock() != forPostUnlock.booleanValue()) {
384                 continue;
385             }
386 
387             boolean userScopeChecked = isUserInScope(userId, service, mCarUserService,
388                     currentUserId);
389             boolean triggerChecked = service.shouldStartAsap() || unlocked;
390 
391             if (userScopeChecked && triggerChecked) {
392                 startOrBindService(service, user);
393             }
394         }
395     }
396 
397     @SuppressLint("NewApi")
startOrBindServicesIfNeeded()398     private void startOrBindServicesIfNeeded() {
399         // Start/bind service for system user.
400         startOrBindServicesForUser(UserHandle.SYSTEM, /* forPostUnlock= */ null);
401 
402         if (!isPlatformVersionAtLeastU()) {
403             // `user=visible` is not supported before U. Just need to handle the current user.
404             startOrBindServicesForUser(UserHandle.of(ActivityManager.getCurrentUser()),
405                     /* forPostUnlock= */ null);
406         } else {
407             // Start/bind service for all visible users.
408             Set<UserHandle> visibleUsers = mUserManager.getVisibleUsers();
409             for (Iterator<UserHandle> iterator = visibleUsers.iterator(); iterator.hasNext(); ) {
410                 UserHandle userHandle = iterator.next();
411                 startOrBindServicesForUser(userHandle, /* forPostUnlock= */ null);
412             }
413         }
414     }
415 
startOrBindService(VendorServiceInfo service, UserHandle user)416     private void startOrBindService(VendorServiceInfo service, UserHandle user) {
417         ConnectionKey key = ConnectionKey.of(service, user);
418         VendorServiceConnection connection = getOrCreateConnection(key);
419         if (!connection.startOrBindService()) {
420             Slogf.e(TAG, "Failed to start or bind service " + service);
421             mConnections.remove(key);
422         }
423     }
424 
stopOrUnbindService(VendorServiceInfo service, UserHandle user)425     private void stopOrUnbindService(VendorServiceInfo service, UserHandle user) {
426         ConnectionKey key = ConnectionKey.of(service, user);
427         VendorServiceConnection connection = mConnections.get(key);
428         if (connection != null) {
429             connection.stopOrUnbindService();
430         }
431     }
432 
433     /**
434      * Unbinds the VendorServiceController from all the services with the given {@code packageName}
435      * and running as {@code userId}.
436      */
stopOrUnbindService(String packageName, @UserIdInt int userId)437     private void stopOrUnbindService(String packageName, @UserIdInt int userId) {
438         for (VendorServiceConnection connection : mConnections.values()) {
439             if (connection.isUser(userId)
440                     && packageName.equals(connection.mVendorServiceInfo.getIntent().getComponent()
441                     .getPackageName())) {
442                 Slogf.d(TAG, "Stopping the connection to service %s",
443                          connection.mVendorServiceInfo);
444                 connection.stopOrUnbindService();
445             }
446         }
447     }
448 
getOrCreateConnection(ConnectionKey key)449     private VendorServiceConnection getOrCreateConnection(ConnectionKey key) {
450         VendorServiceConnection connection = mConnections.get(key);
451         if (connection == null) {
452             connection = new VendorServiceConnection(mContext, mHandler, key.mVendorServiceInfo,
453                     key.mUserHandle);
454             mConnections.put(key, connection);
455         }
456 
457         return connection;
458     }
459 
460     /** Loads data from XML resources and returns true if any services needs to be started/bound. */
loadXmlConfiguration()461     private boolean loadXmlConfiguration() {
462         final Resources res = mContext.getResources();
463         for (String rawServiceInfo: res.getStringArray(R.array.config_earlyStartupServices)) {
464             if (TextUtils.isEmpty(rawServiceInfo)) {
465                 continue;
466             }
467             VendorServiceInfo service = VendorServiceInfo.parse(rawServiceInfo);
468             // `user=visible` and `user=backgroundVisible` are not supported before U.
469             // Log an error and ignore the service.
470             if ((service.isVisibleUserService() || service.isBackgroundVisibleUserService())
471                     && !service.isAllUserService() && !isPlatformVersionAtLeastU()) {
472                 Slogf.e(TAG, "user=visible and user=backgroundVisible are not supported in "
473                         + "this platform version. %s is ignored. Check your config.xml file.",
474                         service.toShortString());
475                 continue;
476             }
477             mVendorServiceInfos.add(service);
478             if (DBG) {
479                 Slogf.i(TAG, "Registered vendor service: " + service);
480             }
481         }
482         Slogf.i(TAG, "Found " + mVendorServiceInfos.size()
483                 + " services to be started/bound");
484 
485         return !mVendorServiceInfos.isEmpty();
486     }
487 
488     /**
489      * Represents connection to the vendor service.
490      */
491     @VisibleForTesting
492     public static final class VendorServiceConnection implements ServiceConnection, Executor {
493         private static final int INITIAL_REBIND_DELAY_MS = 4000; // 4 sec.
494         private static final int DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT = 5 * 60 * 1000; // 5 min.
495         private static final int MSG_REBIND = 0;
496         private static final int MSG_FAILURE_COUNTER_RESET = 1;
497 
498         private int mRecentFailures = 0;
499         private boolean mBound = false;
500         private boolean mStarted = false;
501         private boolean mStopRequested = false;
502         private final VendorServiceInfo mVendorServiceInfo;
503         private final UserHandle mUser;
504         private final CarUserService mCarUserService;
505         private final Context mUserContext;
506         private final Handler mHandler;
507         private final Handler mFailureHandler;
508 
VendorServiceConnection(Context context, Handler handler, VendorServiceInfo vendorServiceInfo, UserHandle user)509         VendorServiceConnection(Context context, Handler handler,
510                 VendorServiceInfo vendorServiceInfo, UserHandle user) {
511             mHandler = handler;
512             mVendorServiceInfo = vendorServiceInfo;
513             mUser = user;
514             mUserContext = context.createContextAsUser(mUser, /* flags= */ 0);
515             mCarUserService = CarLocalServices.getService(CarUserService.class);
516 
517             mFailureHandler = new Handler(handler.getLooper()) {
518                 @Override
519                 public void handleMessage(Message msg) {
520                     handleFailureMessage(msg);
521                 }
522             };
523         }
524 
525         @Override
toString()526         public String toString() {
527             return "VendorServiceConnection[user=" + mUser
528                     + ", service=" + mVendorServiceInfo + "]";
529         }
530 
531         @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)532         public void dump(PrintWriter writer) {
533             writer.printf("%s, mRecentFailures=%d, mBound=%b, mStarted=%b, mStopRequested=%b\n",
534                     toString(), mRecentFailures, mBound, mStarted, mStopRequested);
535         }
536 
isUser(@serIdInt int userId)537         private boolean isUser(@UserIdInt int userId) {
538             return mUser.getIdentifier() == userId;
539         }
540 
startOrBindService()541         boolean startOrBindService() {
542             if (mStarted || mBound) {
543                 return true;  // Already started or bound
544             }
545 
546             if (DBG) {
547                 Slogf.d(TAG, "startOrBindService "
548                         + mVendorServiceInfo.toShortString() + ", as user: " + mUser + ", bind: "
549                         + mVendorServiceInfo.shouldBeBound());
550             }
551             mStopRequested = false;
552 
553             Intent intent = mVendorServiceInfo.getIntent();
554             if (mVendorServiceInfo.shouldBeBound()) {
555                 return mUserContext.bindService(intent, BIND_AUTO_CREATE, /* executor= */ this,
556                         /* conn= */ this);
557             } else if (mVendorServiceInfo.shouldBeStartedInForeground()) {
558                 mStarted = mUserContext.startForegroundService(intent) != null;
559                 return mStarted;
560             } else {
561                 mStarted = mUserContext.startService(intent) != null;
562                 return mStarted;
563             }
564         }
565 
stopOrUnbindService()566         void stopOrUnbindService() {
567             mStopRequested = true;
568             if (mStarted) {
569                 if (DBG) Slogf.d(TAG, "Stopping %s", this);
570                 mUserContext.stopService(mVendorServiceInfo.getIntent());
571                 mStarted = false;
572             } else if (mBound) {
573                 if (DBG) Slogf.d(TAG, "Unbinding %s", this);
574                 mUserContext.unbindService(this);
575                 mBound = false;
576             }
577         }
578 
579         @Override // From Executor
execute(Runnable command)580         public void execute(Runnable command) {
581             mHandler.post(command);
582         }
583 
584         @Override
onServiceConnected(ComponentName name, IBinder service)585         public void onServiceConnected(ComponentName name, IBinder service) {
586             mBound = true;
587             if (DBG) {
588                 Slogf.d(TAG, "onServiceConnected, name: %s", name);
589             }
590             if (mStopRequested) {
591                 stopOrUnbindService();
592             }
593         }
594 
595         @Override
onServiceDisconnected(ComponentName name)596         public void onServiceDisconnected(ComponentName name) {
597             mBound = false;
598             if (DBG) {
599                 Slogf.d(TAG, "onServiceDisconnected, name: " + name);
600             }
601             tryToRebind();
602         }
603 
604         @Override
onBindingDied(ComponentName name)605         public void onBindingDied(ComponentName name) {
606             mBound = false;
607             if (DBG) {
608                 Slogf.d(TAG, "onBindingDied, name: " + name);
609             }
610             tryToRebind();
611         }
612 
tryToRebind()613         private void tryToRebind() {
614             if (mStopRequested) {
615                 return;
616             }
617 
618             if (mFailureHandler.hasMessages(MSG_REBIND)) {
619                 if (DBG) {
620                     Slogf.d(TAG, "Rebind already scheduled for "
621                             + mVendorServiceInfo.toShortString());
622                 }
623                 return;
624             }
625 
626             int currentUserId = ActivityManager.getCurrentUser();
627             if (isUserInScope(mUser.getIdentifier(), mVendorServiceInfo, mCarUserService,
628                     currentUserId)) {
629                 // Double the delay after each failure.
630                 int rebindDelay = INITIAL_REBIND_DELAY_MS * (1 << mRecentFailures);
631                 Slogf.i(TAG, "tryToRebind(): after " + mRecentFailures + " recent failures,"
632                         + " trying to rebind service " + mVendorServiceInfo.toShortString()
633                         + " for user " + mUser.getIdentifier() + " in " + rebindDelay + "ms");
634                 mFailureHandler.sendMessageDelayed(
635                         mFailureHandler.obtainMessage(MSG_REBIND), rebindDelay);
636                 scheduleResetFailureCounter();
637             } else {
638                 Slogf.w(TAG, "No need to rebind anymore as the service no longer satisfies "
639                         + " the user scope.");
640             }
641         }
642 
scheduleResetFailureCounter()643         private void scheduleResetFailureCounter() {
644             mFailureHandler.removeMessages(MSG_FAILURE_COUNTER_RESET);
645             // Reset the failure counter after the timeout. We take the max, to ensure
646             // that we are not resetting the counter before exhausting all retries.
647             int failureCounterResetTimeout =
648                     INITIAL_REBIND_DELAY_MS * (1 << (mVendorServiceInfo.getMaxRetries() + 1));
649             failureCounterResetTimeout =
650                     failureCounterResetTimeout > DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT
651                             ? failureCounterResetTimeout : DEFAULT_FAILURE_COUNTER_RESET_TIMEOUT;
652             mFailureHandler.sendMessageDelayed(
653                     mFailureHandler.obtainMessage(MSG_FAILURE_COUNTER_RESET),
654                     failureCounterResetTimeout);
655         }
656 
handleFailureMessage(Message msg)657         private void handleFailureMessage(Message msg) {
658             switch (msg.what) {
659                 case MSG_REBIND: {
660                     if (mBound) {
661                         Slogf.d(TAG, "Service " + mVendorServiceInfo.toShortString()
662                                 + " is already bound. Ignoring MSG_REBIND");
663                     } else if (mRecentFailures < mVendorServiceInfo.getMaxRetries()) {
664                         Slogf.i(TAG, "Attempting to rebind to the service "
665                                 + mVendorServiceInfo.toShortString() + " (" + (mRecentFailures + 1)
666                                 + " out of " + mVendorServiceInfo.getMaxRetries() + " max tries)");
667                         ++mRecentFailures;
668                         startOrBindService();
669                     } else {
670                         Slogf.w(TAG, "Exceeded maximum number of attempts ("
671                                 + mVendorServiceInfo.getMaxRetries() + ") to rebind to the service "
672                                 + mVendorServiceInfo.toShortString());
673                     }
674                     break;
675                 }
676                 case MSG_FAILURE_COUNTER_RESET:
677                     mRecentFailures = 0;
678                     break;
679                 default:
680                     Slogf.e(TAG, "Unexpected message received in failure handler: " + msg.what);
681             }
682         }
683     }
684 
685     /** Defines a key in the HashMap to store connection on per user and vendor service basis */
686     private static class ConnectionKey {
687         private final UserHandle mUserHandle;
688         private final VendorServiceInfo mVendorServiceInfo;
689 
ConnectionKey(VendorServiceInfo service, UserHandle user)690         private ConnectionKey(VendorServiceInfo service, UserHandle user) {
691             mVendorServiceInfo = service;
692             mUserHandle = user;
693         }
694 
of(VendorServiceInfo service, UserHandle user)695         static ConnectionKey of(VendorServiceInfo service, UserHandle user) {
696             return new ConnectionKey(service, user);
697         }
698 
699         @Override
equals(Object o)700         public boolean equals(Object o) {
701             if (this == o) {
702                 return true;
703             }
704             if (!(o instanceof ConnectionKey)) {
705                 return false;
706             }
707             ConnectionKey that = (ConnectionKey) o;
708             return Objects.equals(mUserHandle, that.mUserHandle)
709                     && Objects.equals(mVendorServiceInfo, that.mVendorServiceInfo);
710         }
711 
712         @Override
hashCode()713         public int hashCode() {
714             return Objects.hash(mUserHandle, mVendorServiceInfo);
715         }
716     }
717 }
718