Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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; |
| 18 | |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 19 | import android.car.IPerUserCarService; |
Antonio Kantek | f700753 | 2020-03-17 10:37:58 -0700 | [diff] [blame] | 20 | import android.car.user.CarUserManager; |
| 21 | import android.car.user.CarUserManager.UserLifecycleListener; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 22 | import android.content.ComponentName; |
| 23 | import android.content.Context; |
| 24 | import android.content.Intent; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 25 | import android.content.ServiceConnection; |
| 26 | import android.os.IBinder; |
| 27 | import android.os.UserHandle; |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 28 | import android.util.IndentingPrintWriter; |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 29 | import android.util.Slog; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 30 | |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 31 | import com.android.car.user.CarUserService; |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 32 | import com.android.internal.annotations.GuardedBy; |
| 33 | |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 34 | import java.util.ArrayList; |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 35 | import java.util.List; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 36 | |
| 37 | /** |
| 38 | * A Helper class that helps with the following: |
| 39 | * 1. Provide methods to Bind/Unbind to the {@link PerUserCarService} as the current User |
| 40 | * 2. Set up a listener to UserSwitch Broadcasts and call clients that have registered callbacks. |
| 41 | * |
| 42 | */ |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 43 | public class PerUserCarServiceHelper implements CarServiceBase { |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 44 | |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 45 | private static final String TAG = "PerUserCarSvcHelper"; |
| 46 | private static boolean DBG = false; |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 47 | |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 48 | private final Context mContext; |
| 49 | private final CarUserService mUserService; |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 50 | private IPerUserCarService mPerUserCarService; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 51 | // listener to call on a ServiceConnection to PerUserCarService |
| 52 | private List<ServiceCallback> mServiceCallbacks; |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 53 | private final Object mServiceBindLock = new Object(); |
| 54 | @GuardedBy("mServiceBindLock") |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 55 | private boolean mBound; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 56 | |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 57 | public PerUserCarServiceHelper(Context context, CarUserService userService) { |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 58 | mContext = context; |
| 59 | mServiceCallbacks = new ArrayList<>(); |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 60 | mUserService = userService; |
Antonio Kantek | f700753 | 2020-03-17 10:37:58 -0700 | [diff] [blame] | 61 | mUserService.addUserLifecycleListener(mUserLifecycleListener); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 62 | } |
| 63 | |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 64 | @Override |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 65 | public void init() { |
| 66 | synchronized (mServiceBindLock) { |
| 67 | bindToPerUserCarService(); |
| 68 | } |
| 69 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 70 | |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 71 | @Override |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 72 | public void release() { |
| 73 | synchronized (mServiceBindLock) { |
| 74 | unbindFromPerUserCarService(); |
Antonio Kantek | f700753 | 2020-03-17 10:37:58 -0700 | [diff] [blame] | 75 | mUserService.removeUserLifecycleListener(mUserLifecycleListener); |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 76 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 77 | } |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 78 | |
Antonio Kantek | f700753 | 2020-03-17 10:37:58 -0700 | [diff] [blame] | 79 | private final UserLifecycleListener mUserLifecycleListener = event -> { |
| 80 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 81 | Slog.d(TAG, "onEvent(" + event + ")"); |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 82 | } |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 83 | if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING == event.getEventType()) { |
| 84 | List<ServiceCallback> callbacks; |
| 85 | int userId = event.getUserId(); |
| 86 | if (DBG) { |
| 87 | Slog.d(TAG, "User Switch Happened. New User" + userId); |
| 88 | } |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 89 | |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 90 | // Before unbinding, notify the callbacks about unbinding from the service |
| 91 | // so the callbacks can clean up their state through the binder before the service is |
| 92 | // killed. |
| 93 | synchronized (mServiceBindLock) { |
| 94 | // copy the callbacks |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 95 | callbacks = new ArrayList<>(mServiceCallbacks); |
| 96 | } |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 97 | // call them |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 98 | for (ServiceCallback callback : callbacks) { |
| 99 | callback.onPreUnbind(); |
| 100 | } |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 101 | // unbind from the service running as the previous user. |
Christophe Koessler | 47a64c4 | 2020-12-09 14:19:34 -0800 | [diff] [blame] | 102 | unbindFromPerUserCarService(); |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 103 | // bind to the service running as the new user |
| 104 | bindToPerUserCarService(); |
Christophe Koessler | 47a64c4 | 2020-12-09 14:19:34 -0800 | [diff] [blame] | 105 | } |
Mayank Garg | 31e7304 | 2020-01-23 00:10:38 -0800 | [diff] [blame] | 106 | }; |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 107 | |
| 108 | /** |
| 109 | * ServiceConnection to detect connecting/disconnecting to {@link PerUserCarService} |
| 110 | */ |
| 111 | private final ServiceConnection mUserServiceConnection = new ServiceConnection() { |
| 112 | // On connecting to the service, get the binder object to the CarBluetoothService |
| 113 | @Override |
| 114 | public void onServiceConnected(ComponentName componentName, IBinder service) { |
| 115 | List<ServiceCallback> callbacks; |
| 116 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 117 | Slog.d(TAG, "Connected to User Service"); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 118 | } |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 119 | mPerUserCarService = IPerUserCarService.Stub.asInterface(service); |
| 120 | if (mPerUserCarService != null) { |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 121 | synchronized (mServiceBindLock) { |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 122 | // copy the callbacks |
| 123 | callbacks = new ArrayList<>(mServiceCallbacks); |
| 124 | } |
| 125 | // call them |
| 126 | for (ServiceCallback callback : callbacks) { |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 127 | callback.onServiceConnected(mPerUserCarService); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | @Override |
| 133 | public void onServiceDisconnected(ComponentName componentName) { |
| 134 | List<ServiceCallback> callbacks; |
| 135 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 136 | Slog.d(TAG, "Disconnected from User Service"); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 137 | } |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 138 | synchronized (mServiceBindLock) { |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 139 | // copy the callbacks |
| 140 | callbacks = new ArrayList<>(mServiceCallbacks); |
| 141 | } |
| 142 | // call them |
| 143 | for (ServiceCallback callback : callbacks) { |
| 144 | callback.onServiceDisconnected(); |
| 145 | } |
| 146 | } |
| 147 | }; |
| 148 | |
| 149 | /** |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 150 | * Bind to the PerUserCarService {@link PerUserCarService} which is created to run as the |
| 151 | * Current User. |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 152 | */ |
| 153 | private void bindToPerUserCarService() { |
| 154 | if (DBG) { |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 155 | Slog.d(TAG, "Binding to User service"); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 156 | } |
| 157 | Intent startIntent = new Intent(mContext, PerUserCarService.class); |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 158 | synchronized (mServiceBindLock) { |
| 159 | mBound = true; |
| 160 | boolean bindSuccess = mContext.bindServiceAsUser(startIntent, mUserServiceConnection, |
Christophe Koessler | 6914326 | 2021-02-24 21:08:34 +0000 | [diff] [blame^] | 161 | mContext.BIND_AUTO_CREATE, UserHandle.CURRENT); |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 162 | // If valid connection not obtained, unbind |
| 163 | if (!bindSuccess) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 164 | Slog.e(TAG, "bindToPerUserCarService() failed to get valid connection"); |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 165 | unbindFromPerUserCarService(); |
| 166 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Unbind from the {@link PerUserCarService} running as the Current user. |
| 172 | */ |
| 173 | private void unbindFromPerUserCarService() { |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 174 | synchronized (mServiceBindLock) { |
| 175 | // mBound flag makes sure we are unbinding only when the service is bound. |
| 176 | if (mBound) { |
| 177 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 178 | Slog.d(TAG, "Unbinding from User Service"); |
Ram Periathiruvadi | ee7c58a | 2017-05-30 15:31:07 -0700 | [diff] [blame] | 179 | } |
| 180 | mContext.unbindService(mUserServiceConnection); |
| 181 | mBound = false; |
| 182 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 183 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Register a listener that gets called on Connection state changes to the |
| 188 | * {@link PerUserCarService} |
| 189 | * @param listener - Callback to invoke on user switch event. |
| 190 | */ |
| 191 | public void registerServiceCallback(ServiceCallback listener) { |
| 192 | if (listener != null) { |
| 193 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 194 | Slog.d(TAG, "Registering PerUserCarService Listener"); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 195 | } |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 196 | synchronized (mServiceBindLock) { |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 197 | mServiceCallbacks.add(listener); |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Unregister the Service Listener |
| 204 | * @param listener - Callback method to unregister |
| 205 | */ |
| 206 | public void unregisterServiceCallback(ServiceCallback listener) { |
| 207 | if (DBG) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 208 | Slog.d(TAG, "Unregistering PerUserCarService Listener"); |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 209 | } |
| 210 | if (listener != null) { |
Yan Zhu | 211b1fe | 2019-11-06 15:54:19 -0800 | [diff] [blame] | 211 | synchronized (mServiceBindLock) { |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 212 | mServiceCallbacks.remove(listener); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | /** |
| 218 | * Listener to the PerUserCarService connection status that clients need to implement. |
| 219 | */ |
| 220 | public interface ServiceCallback { |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 221 | /** |
| 222 | * Invoked when a service connects. |
| 223 | * |
| 224 | * @param perUserCarService the instance of IPerUserCarService. |
| 225 | */ |
| 226 | void onServiceConnected(IPerUserCarService perUserCarService); |
| 227 | |
| 228 | /** |
| 229 | * Invoked before an unbind call is going to be made. |
| 230 | */ |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 231 | void onPreUnbind(); |
Eric Jeong | a84a682 | 2019-09-17 15:46:38 -0700 | [diff] [blame] | 232 | |
| 233 | /** |
| 234 | * Invoked when a service is crashed or disconnected. |
| 235 | */ |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 236 | void onServiceDisconnected(); |
| 237 | } |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 238 | |
| 239 | @Override |
Felipe Leme | 176a5fd | 2021-01-20 15:48:33 -0800 | [diff] [blame] | 240 | public final void dump(IndentingPrintWriter pw) { |
| 241 | pw.println("PerUserCarServiceHelper"); |
| 242 | pw.increaseIndent(); |
| 243 | synchronized (mServiceBindLock) { |
| 244 | pw.printf("bound: %b\n", mBound); |
| 245 | if (mServiceCallbacks == null) { |
| 246 | pw.println("no callbacks"); |
| 247 | } else { |
| 248 | int size = mServiceCallbacks.size(); |
| 249 | pw.printf("%d callback%s\n", size, (size > 1 ? "s" : "")); |
| 250 | } |
| 251 | } |
| 252 | pw.decreaseIndent(); |
Ram Periathiruvadi | a048c0a | 2017-05-09 07:35:03 -0700 | [diff] [blame] | 253 | } |
Ram Periathiruvadi | acb6024 | 2017-04-13 16:19:09 -0700 | [diff] [blame] | 254 | } |