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