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.content.Context.BIND_AUTO_CREATE; 20 21 import android.app.ActivityManager; 22 import android.car.user.CarUserManager; 23 import android.car.user.CarUserManager.UserLifecycleEvent; 24 import android.car.user.CarUserManager.UserLifecycleListener; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.ServiceConnection; 29 import android.content.res.Resources; 30 import android.os.Debug; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.util.Slog; 40 41 import com.android.car.CarLocalServices; 42 import com.android.car.CarLog; 43 import com.android.car.R; 44 import com.android.car.user.CarUserService; 45 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.List; 49 import java.util.Objects; 50 51 /** 52 * Class that responsible for controlling vendor services that was opted in to be bound/started 53 * by the Car Service. 54 * 55 * <p>Thread-safety note: all code runs in the {@code Handler} provided in the constructor, whenever 56 * possible pass {@link #mHandler} when subscribe for callbacks otherwise redirect code to the 57 * handler. 58 */ 59 class VendorServiceController implements UserLifecycleListener { 60 61 private static final String TAG = CarLog.tagFor(VendorServiceController.class); 62 63 private static final boolean DBG = true; 64 65 private static final int MSG_SWITCH_USER = 1; 66 private static final int MSG_USER_LOCK_CHANGED = 2; 67 68 private final List<VendorServiceInfo> mVendorServiceInfos = new ArrayList<>(); 69 private final HashMap<ConnectionKey, VendorServiceConnection> mConnections = 70 new HashMap<>(); 71 private final Context mContext; 72 private final UserManager mUserManager; 73 private final Handler mHandler; 74 private CarUserService mCarUserService; 75 76 VendorServiceController(Context context, Looper looper)77 VendorServiceController(Context context, Looper looper) { 78 mContext = context; 79 mUserManager = context.getSystemService(UserManager.class); 80 mHandler = new Handler(looper) { 81 @Override 82 public void handleMessage(Message msg) { 83 VendorServiceController.this.handleMessage(msg); 84 } 85 }; 86 } 87 handleMessage(Message msg)88 private void handleMessage(Message msg) { 89 switch (msg.what) { 90 case MSG_SWITCH_USER: { 91 int userId = msg.arg1; 92 doSwitchUser(userId); 93 break; 94 } 95 case MSG_USER_LOCK_CHANGED: { 96 int userId = msg.arg1; 97 boolean locked = msg.arg2 == 1; 98 doUserLockChanged(userId, locked); 99 break; 100 } 101 default: 102 Slog.e(TAG, "Unexpected message " + msg); 103 } 104 } 105 init()106 void init() { 107 if (!loadXmlConfiguration()) { 108 return; // Nothing to do 109 } 110 111 mCarUserService = CarLocalServices.getService(CarUserService.class); 112 mCarUserService.addUserLifecycleListener(this); 113 114 startOrBindServicesIfNeeded(); 115 } 116 release()117 void release() { 118 if (mCarUserService != null) { 119 mCarUserService.removeUserLifecycleListener(this); 120 } 121 122 for (ConnectionKey key : mConnections.keySet()) { 123 stopOrUnbindService(key.mVendorServiceInfo, key.mUserHandle); 124 } 125 mVendorServiceInfos.clear(); 126 mConnections.clear(); 127 } 128 129 @Override onEvent(UserLifecycleEvent event)130 public void onEvent(UserLifecycleEvent event) { 131 if (Log.isLoggable(TAG, Log.DEBUG)) { 132 Slog.d(TAG, "onEvent(" + event + ")"); 133 } 134 // TODO(b/152069895): Use USER_LIFECYCLE_EVENT_TYPE_UNLOCKED and not care about the 135 // deprecated unlock=false scenario. 136 if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING == event.getEventType()) { 137 Message msg = mHandler.obtainMessage( 138 MSG_USER_LOCK_CHANGED, 139 event.getUserId(), 140 /* unlocked= */ 1); 141 mHandler.executeOrSendMessage(msg); 142 } else if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING == event.getEventType()) { 143 mHandler.removeMessages(MSG_SWITCH_USER); 144 Message msg = mHandler.obtainMessage( 145 MSG_SWITCH_USER, 146 event.getUserId(), 147 /* unlocked= */ 0); 148 mHandler.executeOrSendMessage(msg); 149 } 150 } 151 doSwitchUser(int userId)152 private void doSwitchUser(int userId) { 153 // Stop all services which which do not run under foreground or system user. 154 final int fgUser = ActivityManager.getCurrentUser(); 155 if (fgUser != userId) { 156 Slog.w(TAG, "Received userSwitch event for user " + userId 157 + " while current foreground user is " + fgUser + "." 158 + " Ignore the switch user event."); 159 return; 160 } 161 162 for (VendorServiceConnection connection : mConnections.values()) { 163 final int connectedUserId = connection.mUser.getIdentifier(); 164 if (connectedUserId != UserHandle.USER_SYSTEM && connectedUserId != userId) { 165 connection.stopOrUnbindService(); 166 } 167 } 168 169 if (userId != UserHandle.USER_SYSTEM) { 170 startOrBindServicesForUser(UserHandle.of(userId)); 171 } else { 172 Slog.e(TAG, "Unexpected to receive switch user event for system user"); 173 } 174 } 175 doUserLockChanged(int userId, boolean unlocked)176 private void doUserLockChanged(int userId, boolean unlocked) { 177 final int currentUserId = ActivityManager.getCurrentUser(); 178 179 if (DBG) { 180 Slog.i(TAG, "onUserLockedChanged, user: " + userId 181 + ", unlocked: " + unlocked + ", currentUser: " + currentUserId); 182 } 183 if (unlocked && (userId == currentUserId || userId == UserHandle.USER_SYSTEM)) { 184 startOrBindServicesForUser(UserHandle.of(userId)); 185 } else if (!unlocked && userId != UserHandle.USER_SYSTEM) { 186 for (ConnectionKey key : mConnections.keySet()) { 187 if (key.mUserHandle.getIdentifier() == userId) { 188 stopOrUnbindService(key.mVendorServiceInfo, key.mUserHandle); 189 } 190 } 191 } 192 } 193 startOrBindServicesForUser(UserHandle user)194 private void startOrBindServicesForUser(UserHandle user) { 195 boolean unlocked = mUserManager.isUserUnlockingOrUnlocked(user); 196 boolean systemUser = UserHandle.SYSTEM.equals(user); 197 for (VendorServiceInfo service: mVendorServiceInfos) { 198 boolean userScopeChecked = (!systemUser && service.isForegroundUserService()) 199 || (systemUser && service.isSystemUserService()); 200 boolean triggerChecked = service.shouldStartAsap() 201 || (unlocked && service.shouldStartOnUnlock()); 202 203 if (userScopeChecked && triggerChecked) { 204 startOrBindService(service, user); 205 } 206 } 207 } 208 startOrBindServicesIfNeeded()209 private void startOrBindServicesIfNeeded() { 210 int userId = ActivityManager.getCurrentUser(); 211 startOrBindServicesForUser(UserHandle.SYSTEM); 212 if (userId > 0) { 213 startOrBindServicesForUser(UserHandle.of(userId)); 214 } 215 } 216 startOrBindService(VendorServiceInfo service, UserHandle user)217 private void startOrBindService(VendorServiceInfo service, UserHandle user) { 218 ConnectionKey key = ConnectionKey.of(service, user); 219 VendorServiceConnection connection = getOrCreateConnection(key); 220 if (!connection.startOrBindService()) { 221 Slog.e(TAG, "Failed to start or bind service " + service); 222 mConnections.remove(key); 223 } 224 } 225 stopOrUnbindService(VendorServiceInfo service, UserHandle user)226 private void stopOrUnbindService(VendorServiceInfo service, UserHandle user) { 227 ConnectionKey key = ConnectionKey.of(service, user); 228 VendorServiceConnection connection = mConnections.get(key); 229 if (connection != null) { 230 connection.stopOrUnbindService(); 231 } 232 } 233 getOrCreateConnection(ConnectionKey key)234 private VendorServiceConnection getOrCreateConnection(ConnectionKey key) { 235 VendorServiceConnection connection = mConnections.get(key); 236 if (connection == null) { 237 connection = new VendorServiceConnection(mContext, mHandler, key.mVendorServiceInfo, 238 key.mUserHandle); 239 mConnections.put(key, connection); 240 } 241 242 return connection; 243 } 244 245 /** Loads data from XML resources and returns true if any services needs to be started/bound. */ loadXmlConfiguration()246 private boolean loadXmlConfiguration() { 247 final Resources res = mContext.getResources(); 248 for (String rawServiceInfo: res.getStringArray(R.array.config_earlyStartupServices)) { 249 if (TextUtils.isEmpty(rawServiceInfo)) { 250 continue; 251 } 252 VendorServiceInfo service = VendorServiceInfo.parse(rawServiceInfo); 253 mVendorServiceInfos.add(service); 254 if (DBG) { 255 Slog.i(TAG, "Registered vendor service: " + service); 256 } 257 } 258 Slog.i(TAG, "Found " + mVendorServiceInfos.size() 259 + " services to be started/bound"); 260 261 return !mVendorServiceInfos.isEmpty(); 262 } 263 264 /** 265 * Represents connection to the vendor service. 266 */ 267 private static class VendorServiceConnection implements ServiceConnection { 268 private static final int REBIND_DELAY_MS = 5000; 269 private static final int MAX_RECENT_FAILURES = 5; 270 private static final int FAILURE_COUNTER_RESET_TIMEOUT = 5 * 60 * 1000; // 5 min. 271 private static final int MSG_REBIND = 0; 272 private static final int MSG_FAILURE_COUNTER_RESET = 1; 273 274 private int mRecentFailures = 0; 275 private boolean mBound = false; 276 private boolean mStarted = false; 277 private boolean mStopRequested = false; 278 private final VendorServiceInfo mVendorServiceInfo; 279 private final Context mContext; 280 private final UserHandle mUser; 281 private final Handler mHandler; 282 private final Handler mFailureHandler; 283 VendorServiceConnection(Context context, Handler handler, VendorServiceInfo vendorServiceInfo, UserHandle user)284 VendorServiceConnection(Context context, Handler handler, 285 VendorServiceInfo vendorServiceInfo, UserHandle user) { 286 mContext = context; 287 mHandler = handler; 288 mVendorServiceInfo = vendorServiceInfo; 289 mUser = user; 290 291 mFailureHandler = new Handler(handler.getLooper()) { 292 @Override 293 public void handleMessage(Message msg) { 294 handleFailureMessage(msg); 295 } 296 }; 297 } 298 startOrBindService()299 boolean startOrBindService() { 300 if (mStarted || mBound) { 301 return true; // Already started or bound 302 } 303 304 if (DBG) { 305 Slog.d(TAG, "startOrBindService " 306 + mVendorServiceInfo.toShortString() + ", as user: " + mUser + ", bind: " 307 + mVendorServiceInfo.shouldBeBound() + ", stack: " + Debug.getCallers(5)); 308 } 309 mStopRequested = false; 310 311 Intent intent = mVendorServiceInfo.getIntent(); 312 if (mVendorServiceInfo.shouldBeBound()) { 313 return mContext.bindServiceAsUser(intent, this, BIND_AUTO_CREATE, mHandler, mUser); 314 } else if (mVendorServiceInfo.shouldBeStartedInForeground()) { 315 mStarted = mContext.startForegroundServiceAsUser(intent, mUser) != null; 316 return mStarted; 317 } else { 318 mStarted = mContext.startServiceAsUser(intent, mUser) != null; 319 return mStarted; 320 } 321 } 322 stopOrUnbindService()323 void stopOrUnbindService() { 324 mStopRequested = true; 325 if (mStarted) { 326 mContext.stopServiceAsUser(mVendorServiceInfo.getIntent(), mUser); 327 mStarted = false; 328 } else if (mBound) { 329 mContext.unbindService(this); 330 mBound = false; 331 } 332 } 333 334 @Override onServiceConnected(ComponentName name, IBinder service)335 public void onServiceConnected(ComponentName name, IBinder service) { 336 mBound = true; 337 if (DBG) { 338 Slog.d(TAG, "onServiceConnected, name: " + name); 339 } 340 if (mStopRequested) { 341 stopOrUnbindService(); 342 } 343 } 344 345 @Override onServiceDisconnected(ComponentName name)346 public void onServiceDisconnected(ComponentName name) { 347 mBound = false; 348 if (DBG) { 349 Slog.d(TAG, "onServiceDisconnected, name: " + name); 350 } 351 tryToRebind(); 352 } 353 354 @Override onBindingDied(ComponentName name)355 public void onBindingDied(ComponentName name) { 356 mBound = false; 357 tryToRebind(); 358 } 359 tryToRebind()360 private void tryToRebind() { 361 if (mStopRequested) { 362 return; 363 } 364 365 if (UserHandle.of(ActivityManager.getCurrentUser()).equals(mUser) 366 || UserHandle.SYSTEM.equals(mUser)) { 367 mFailureHandler.sendMessageDelayed( 368 mFailureHandler.obtainMessage(MSG_REBIND), REBIND_DELAY_MS); 369 scheduleResetFailureCounter(); 370 } else { 371 Slog.w(TAG, "No need to rebind anymore as the user " + mUser 372 + " is no longer in foreground."); 373 } 374 } 375 scheduleResetFailureCounter()376 private void scheduleResetFailureCounter() { 377 mFailureHandler.removeMessages(MSG_FAILURE_COUNTER_RESET); 378 mFailureHandler.sendMessageDelayed( 379 mFailureHandler.obtainMessage(MSG_FAILURE_COUNTER_RESET), 380 FAILURE_COUNTER_RESET_TIMEOUT); 381 } 382 handleFailureMessage(Message msg)383 private void handleFailureMessage(Message msg) { 384 switch (msg.what) { 385 case MSG_REBIND: { 386 if (mRecentFailures < MAX_RECENT_FAILURES && !mBound) { 387 Slog.i(TAG, "Attempting to rebind to the service " 388 + mVendorServiceInfo.toShortString()); 389 ++mRecentFailures; 390 startOrBindService(); 391 } else { 392 Slog.w(TAG, "Exceeded maximum number of attempts to rebind" 393 + "to the service " + mVendorServiceInfo.toShortString()); 394 } 395 break; 396 } 397 case MSG_FAILURE_COUNTER_RESET: 398 mRecentFailures = 0; 399 break; 400 default: 401 Slog.e(TAG, "Unexpected message received in failure handler: " + msg.what); 402 } 403 } 404 } 405 406 /** Defines a key in the HashMap to store connection on per user and vendor service basis */ 407 private static class ConnectionKey { 408 private final UserHandle mUserHandle; 409 private final VendorServiceInfo mVendorServiceInfo; 410 ConnectionKey(VendorServiceInfo service, UserHandle user)411 private ConnectionKey(VendorServiceInfo service, UserHandle user) { 412 mVendorServiceInfo = service; 413 mUserHandle = user; 414 } 415 of(VendorServiceInfo service, UserHandle user)416 static ConnectionKey of(VendorServiceInfo service, UserHandle user) { 417 return new ConnectionKey(service, user); 418 } 419 420 @Override equals(Object o)421 public boolean equals(Object o) { 422 if (this == o) { 423 return true; 424 } 425 if (!(o instanceof ConnectionKey)) { 426 return false; 427 } 428 ConnectionKey that = (ConnectionKey) o; 429 return Objects.equals(mUserHandle, that.mUserHandle) 430 && Objects.equals(mVendorServiceInfo, that.mVendorServiceInfo); 431 } 432 433 @Override hashCode()434 public int hashCode() { 435 return Objects.hash(mUserHandle, mVendorServiceInfo); 436 } 437 } 438 } 439