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