PerUserCarServiceHelper.java revision 444ff24b155485e03a45eef8f23bfda0fc8c1bbf
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
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 com.android.internal.annotations.GuardedBy;
31
32import java.io.PrintWriter;
33import java.util.List;
34import 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 */
42public 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
56    public PerUserCarServiceHelper(Context context) {
57        mContext = context;
58        mServiceCallbacks = new ArrayList<>();
59        mReceiver = new UserSwitchBroadcastReceiver();
60        setupUserSwitchListener();
61    }
62
63    @Override
64    public synchronized void init() {
65        bindToPerUserCarService();
66    }
67
68    @Override
69    public synchronized void release() {
70        unbindFromPerUserCarService();
71    }
72
73    /**
74     * Setting up the intent filter for
75     * 2. UserSwitch events
76     */
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
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     */
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     */
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     */
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     */
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
236        void onServiceConnected(ICarUserService carUserService);
237        // Before an unbind call is going to be made.
238        void onPreUnbind();
239        // When Service crashed or disconnected
240        void onServiceDisconnected();
241    }
242
243    @Override
244    public synchronized void dump(PrintWriter writer) {
245
246    }
247
248
249}
250
251