PerUserCarServiceHelper.java revision 6bd897fd7519d2939edda09cc454943b76ea663d
1659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry/* 2659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * Copyright (C) 2015 The Android Open Source Project 3659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * 4659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * Licensed under the Apache License, Version 2.0 (the "License"); 5659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * you may not use this file except in compliance with the License. 6659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * You may obtain a copy of the License at 7659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * 8659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * http://www.apache.org/licenses/LICENSE-2.0 9659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * 10659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * Unless required by applicable law or agreed to in writing, software 11659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * distributed under the License is distributed on an "AS IS" BASIS, 12659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * See the License for the specific language governing permissions and 14659cdedb532e675da5676d40ee39278aadd8f0a1Paul Berry * limitations under the License. 15 */ 16 17package com.android.car; 18 19import android.car.ICarUserService; 20import android.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.ServiceConnection; 26import android.os.IBinder; 27import android.os.UserHandle; 28import android.util.Log; 29 30import java.util.List; 31import java.util.ArrayList; 32 33/** 34 * A Helper class that helps with the following: 35 * 1. Provide methods to Bind/Unbind to the {@link PerUserCarService} as the current User 36 * 2. Set up a listener to UserSwitch Broadcasts and call clients that have registered callbacks. 37 * 38 */ 39public class PerUserCarServiceHelper { 40 private static final String TAG = "PerUserCarSvcHelper"; 41 private static boolean DBG = false; 42 private Context mContext; 43 private ICarUserService mCarUserService; 44 // listener to call on a ServiceConnection to PerUserCarService 45 private List<ServiceCallback> mServiceCallbacks; 46 private UserSwitchBroadcastReceiver mReceiver; 47 private IntentFilter mUserSwitchFilter; 48 private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; 49 50 public PerUserCarServiceHelper(Context context) { 51 mContext = context; 52 mServiceCallbacks = new ArrayList<>(); 53 mReceiver = new UserSwitchBroadcastReceiver(); 54 setupUserSwitchListener(); 55 } 56 57 public synchronized void init() { 58 bindToPerUserCarService(); 59 } 60 61 public synchronized void release() { 62 unbindFromPerUserCarService(); 63 } 64 /** 65 * Setting up the intent filter for 66 * 2. UserSwitch events 67 */ 68 private void setupUserSwitchListener() { 69 mUserSwitchFilter = new IntentFilter(); 70 mUserSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED); 71 mContext.registerReceiver(mReceiver, mUserSwitchFilter); 72 if (DBG) { 73 Log.d(TAG, "UserSwitch Listener Registered"); 74 } 75 } 76 77 /** 78 * UserSwitchBroadcastReceiver receives broadcasts on User account switches. 79 */ 80 public class UserSwitchBroadcastReceiver extends BroadcastReceiver { 81 @Override 82 public void onReceive(Context context, Intent intent) { 83 List<ServiceCallback> callbacks; 84 if (DBG) { 85 Log.d(TAG, "User Switch Happened"); 86 boolean userSwitched = intent.getAction().equals( 87 Intent.ACTION_USER_SWITCHED); 88 89 int user = intent.getExtras().getInt(EXTRA_USER_HANDLE); 90 if (userSwitched) { 91 Log.d(TAG, "New User " + user); 92 } 93 } 94 // Before unbinding, notify the callbacks about unbinding from the service 95 // so the callbacks can clean up their state through the binder before the service is 96 // killed. 97 synchronized (this) { 98 // copy the callbacks 99 callbacks = new ArrayList<>(mServiceCallbacks); 100 } 101 // call them 102 for (ServiceCallback callback : callbacks) { 103 callback.onPreUnbind(); 104 } 105 // unbind from the service running as the previous user. 106 unbindFromPerUserCarService(); 107 // bind to the service running as the new user 108 bindToPerUserCarService(); 109 } 110 } 111 112 /** 113 * ServiceConnection to detect connecting/disconnecting to {@link PerUserCarService} 114 */ 115 private final ServiceConnection mUserServiceConnection = new ServiceConnection() { 116 // On connecting to the service, get the binder object to the CarBluetoothService 117 @Override 118 public void onServiceConnected(ComponentName componentName, IBinder service) { 119 List<ServiceCallback> callbacks; 120 if (DBG) { 121 Log.d(TAG, "Connected to User Service"); 122 } 123 mCarUserService = ICarUserService.Stub.asInterface(service); 124 if (mCarUserService != null) { 125 synchronized (this) { 126 // copy the callbacks 127 callbacks = new ArrayList<>(mServiceCallbacks); 128 } 129 // call them 130 for (ServiceCallback callback : callbacks) { 131 callback.onServiceConnected(mCarUserService); 132 } 133 } 134 } 135 136 @Override 137 public void onServiceDisconnected(ComponentName componentName) { 138 List<ServiceCallback> callbacks; 139 if (DBG) { 140 Log.d(TAG, "Disconnected from User Service"); 141 } 142 synchronized (this) { 143 // copy the callbacks 144 callbacks = new ArrayList<>(mServiceCallbacks); 145 } 146 // call them 147 for (ServiceCallback callback : callbacks) { 148 callback.onServiceDisconnected(); 149 } 150 } 151 }; 152 153 /** 154 * Bind to the CarUserService {@link PerUserCarService} which is created to run as the Current 155 * User. 156 * 157 */ 158 private void bindToPerUserCarService() { 159 if (DBG) { 160 Log.d(TAG, "Binding to User service"); 161 } 162 Intent startIntent = new Intent(mContext, PerUserCarService.class); 163 // Try binding - if valid connection not obtained, unbind 164 if (!mContext.bindServiceAsUser(startIntent, mUserServiceConnection, 165 mContext.BIND_AUTO_CREATE, UserHandle.CURRENT)) { 166 unbindFromPerUserCarService(); 167 } 168 } 169 170 /** 171 * Unbind from the {@link PerUserCarService} running as the Current user. 172 */ 173 private void unbindFromPerUserCarService() { 174 if (DBG) { 175 Log.d(TAG, "Unbinding from User Service"); 176 } 177 mContext.unbindService(mUserServiceConnection); 178 } 179 180 /** 181 * Register a listener that gets called on Connection state changes to the 182 * {@link PerUserCarService} 183 * @param listener - Callback to invoke on user switch event. 184 */ 185 public void registerServiceCallback(ServiceCallback listener) { 186 if (listener != null) { 187 if (DBG) { 188 Log.d(TAG, "Registering PerUserCarService Listener"); 189 } 190 synchronized (this) { 191 mServiceCallbacks.add(listener); 192 } 193 } 194 } 195 196 /** 197 * Unregister the Service Listener 198 * @param listener - Callback method to unregister 199 */ 200 public void unregisterServiceCallback(ServiceCallback listener) { 201 if (DBG) { 202 Log.d(TAG, "Unregistering PerUserCarService Listener"); 203 } 204 if (listener != null) { 205 synchronized (this) { 206 mServiceCallbacks.remove(listener); 207 } 208 } 209 } 210 211 /** 212 * Listener to the PerUserCarService connection status that clients need to implement. 213 */ 214 public interface ServiceCallback { 215 // When Service Connects 216 void onServiceConnected(ICarUserService carUserService); 217 // Before an unbind call is going to be made. 218 void onPreUnbind(); 219 // When Service crashed or disconnected 220 void onServiceDisconnected(); 221 } 222} 223 224