Home | History | Annotate | Download | only in car
      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 
     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