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 19 import android.car.ICarUserService; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.ServiceConnection; 26 import android.os.IBinder; 27 import android.os.UserHandle; 28 import android.util.Log; 29 30 import com.android.internal.annotations.GuardedBy; 31 32 import java.io.PrintWriter; 33 import java.util.List; 34 import java.util.ArrayList; 35 36 /** 37 * A Helper class that helps with the following: 38 * 1. Provide methods to Bind/Unbind to the {@link PerUserCarService} as the current User 39 * 2. Set up a listener to UserSwitch Broadcasts and call clients that have registered callbacks. 40 * 41 */ 42 public class PerUserCarServiceHelper implements CarServiceBase { 43 private static final String TAG = "PerUserCarSvcHelper"; 44 private static boolean DBG = false; 45 private Context mContext; 46 private ICarUserService mCarUserService; 47 // listener to call on a ServiceConnection to PerUserCarService 48 private List<ServiceCallback> mServiceCallbacks; 49 private UserSwitchBroadcastReceiver mReceiver; 50 private IntentFilter mUserSwitchFilter; 51 private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; 52 private final Object mServiceBindLock = new Object(); 53 @GuardedBy("mServiceBindLock") 54 private boolean mBound = false; 55 PerUserCarServiceHelper(Context context)56 public PerUserCarServiceHelper(Context context) { 57 mContext = context; 58 mServiceCallbacks = new ArrayList<>(); 59 mReceiver = new UserSwitchBroadcastReceiver(); 60 setupUserSwitchListener(); 61 } 62 63 @Override init()64 public synchronized void init() { 65 bindToPerUserCarService(); 66 } 67 68 @Override release()69 public synchronized void release() { 70 unbindFromPerUserCarService(); 71 } 72 73 /** 74 * Setting up the intent filter for 75 * 2. UserSwitch events 76 */ setupUserSwitchListener()77 private void setupUserSwitchListener() { 78 mUserSwitchFilter = new IntentFilter(); 79 mUserSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED); 80 mContext.registerReceiver(mReceiver, mUserSwitchFilter); 81 if (DBG) { 82 Log.d(TAG, "UserSwitch Listener Registered"); 83 } 84 } 85 86 /** 87 * UserSwitchBroadcastReceiver receives broadcasts on User account switches. 88 */ 89 public class UserSwitchBroadcastReceiver extends BroadcastReceiver { 90 @Override onReceive(Context context, Intent intent)91 public void onReceive(Context context, Intent intent) { 92 List<ServiceCallback> callbacks; 93 if (DBG) { 94 Log.d(TAG, "User Switch Happened"); 95 boolean userSwitched = intent.getAction().equals( 96 Intent.ACTION_USER_SWITCHED); 97 98 int user = intent.getExtras().getInt(EXTRA_USER_HANDLE); 99 if (userSwitched) { 100 Log.d(TAG, "New User " + user); 101 } 102 } 103 // Before unbinding, notify the callbacks about unbinding from the service 104 // so the callbacks can clean up their state through the binder before the service is 105 // killed. 106 synchronized (this) { 107 // copy the callbacks 108 callbacks = new ArrayList<>(mServiceCallbacks); 109 } 110 // call them 111 for (ServiceCallback callback : callbacks) { 112 callback.onPreUnbind(); 113 } 114 // unbind from the service running as the previous user. 115 unbindFromPerUserCarService(); 116 // bind to the service running as the new user 117 bindToPerUserCarService(); 118 } 119 } 120 121 /** 122 * ServiceConnection to detect connecting/disconnecting to {@link PerUserCarService} 123 */ 124 private final ServiceConnection mUserServiceConnection = new ServiceConnection() { 125 // On connecting to the service, get the binder object to the CarBluetoothService 126 @Override 127 public void onServiceConnected(ComponentName componentName, IBinder service) { 128 List<ServiceCallback> callbacks; 129 if (DBG) { 130 Log.d(TAG, "Connected to User Service"); 131 } 132 mCarUserService = ICarUserService.Stub.asInterface(service); 133 if (mCarUserService != null) { 134 synchronized (this) { 135 // copy the callbacks 136 callbacks = new ArrayList<>(mServiceCallbacks); 137 } 138 // call them 139 for (ServiceCallback callback : callbacks) { 140 callback.onServiceConnected(mCarUserService); 141 } 142 } 143 } 144 145 @Override 146 public void onServiceDisconnected(ComponentName componentName) { 147 List<ServiceCallback> callbacks; 148 if (DBG) { 149 Log.d(TAG, "Disconnected from User Service"); 150 } 151 synchronized (this) { 152 // copy the callbacks 153 callbacks = new ArrayList<>(mServiceCallbacks); 154 } 155 // call them 156 for (ServiceCallback callback : callbacks) { 157 callback.onServiceDisconnected(); 158 } 159 } 160 }; 161 162 /** 163 * Bind to the CarUserService {@link PerUserCarService} which is created to run as the Current 164 * User. 165 * 166 */ bindToPerUserCarService()167 private void bindToPerUserCarService() { 168 if (DBG) { 169 Log.d(TAG, "Binding to User service"); 170 } 171 Intent startIntent = new Intent(mContext, PerUserCarService.class); 172 synchronized (mServiceBindLock) { 173 mBound = true; 174 boolean bindSuccess = mContext.bindServiceAsUser(startIntent, mUserServiceConnection, 175 mContext.BIND_AUTO_CREATE, UserHandle.CURRENT); 176 // If valid connection not obtained, unbind 177 if (!bindSuccess) { 178 Log.e(TAG, "bindToPerUserCarService() failed to get valid connection"); 179 unbindFromPerUserCarService(); 180 } 181 } 182 } 183 184 /** 185 * Unbind from the {@link PerUserCarService} running as the Current user. 186 */ unbindFromPerUserCarService()187 private void unbindFromPerUserCarService() { 188 synchronized (mServiceBindLock) { 189 // mBound flag makes sure we are unbinding only when the service is bound. 190 if (mBound) { 191 if (DBG) { 192 Log.d(TAG, "Unbinding from User Service"); 193 } 194 mContext.unbindService(mUserServiceConnection); 195 mBound = false; 196 } 197 } 198 } 199 200 /** 201 * Register a listener that gets called on Connection state changes to the 202 * {@link PerUserCarService} 203 * @param listener - Callback to invoke on user switch event. 204 */ registerServiceCallback(ServiceCallback listener)205 public void registerServiceCallback(ServiceCallback listener) { 206 if (listener != null) { 207 if (DBG) { 208 Log.d(TAG, "Registering PerUserCarService Listener"); 209 } 210 synchronized (this) { 211 mServiceCallbacks.add(listener); 212 } 213 } 214 } 215 216 /** 217 * Unregister the Service Listener 218 * @param listener - Callback method to unregister 219 */ unregisterServiceCallback(ServiceCallback listener)220 public void unregisterServiceCallback(ServiceCallback listener) { 221 if (DBG) { 222 Log.d(TAG, "Unregistering PerUserCarService Listener"); 223 } 224 if (listener != null) { 225 synchronized (this) { 226 mServiceCallbacks.remove(listener); 227 } 228 } 229 } 230 231 /** 232 * Listener to the PerUserCarService connection status that clients need to implement. 233 */ 234 public interface ServiceCallback { 235 // When Service Connects onServiceConnected(ICarUserService carUserService)236 void onServiceConnected(ICarUserService carUserService); 237 // Before an unbind call is going to be made. onPreUnbind()238 void onPreUnbind(); 239 // When Service crashed or disconnected onServiceDisconnected()240 void onServiceDisconnected(); 241 } 242 243 @Override dump(PrintWriter writer)244 public synchronized void dump(PrintWriter writer) { 245 246 } 247 248 249 } 250 251