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