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