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 android.car;
     18 
     19 import android.os.Handler;
     20 import android.os.IBinder;
     21 import android.os.Looper;
     22 import android.os.RemoteException;
     23 
     24 import java.lang.ref.WeakReference;
     25 import java.util.HashMap;
     26 import java.util.Map;
     27 
     28 /**
     29  * CarAppContextManager allows applications to set and listen for the current application context
     30  * like active navigation or active voice command. Usually only one instance of such application
     31  * should run in the system, and other app setting the flag for the matching app should
     32  * lead into other app to stop.
     33  * @hide
     34  */
     35 public class CarAppContextManager implements CarManagerBase {
     36     /**
     37      * Listener to get notification for app getting information on app context change.
     38      */
     39     public interface AppContextChangeListener {
     40         /**
     41          * Application context has changed. Note that {@link CarAppContextManager} instance
     42          * causing the change will not get this notification.
     43          * @param activeContexts
     44          */
     45         void onAppContextChange(int activeContexts);
     46     }
     47 
     48     /**
     49      * Listener to get notification for app getting information on app context ownership loss.
     50      */
     51     public interface AppContextOwnershipChangeListener {
     52         /**
     53          * Lost ownership for the context, which happens when other app has set the context.
     54          * The app losing context should stop the action associated with the context.
     55          * For example, navigaiton app currently running active navigation should stop navigation
     56          * upon getting this for {@link CarAppContextManager#APP_CONTEXT_NAVIGATION}.
     57          * @param context
     58          */
     59         void onAppContextOwnershipLoss(int context);
     60     }
     61 
     62     /** @hide */
     63     public static final int APP_CONTEXT_START_FLAG = 0x1;
     64     /**
     65      * Flag for active navigation.
     66      */
     67     public static final int APP_CONTEXT_NAVIGATION = 0x1;
     68     /**
     69      * Flag for active voice command.
     70      */
     71     public static final int APP_CONTEXT_VOICE_COMMAND = 0x2;
     72     /**
     73      * Update this after adding a new flag.
     74      * @hide
     75      */
     76     public static final int APP_CONTEXT_END_FLAG = 0x2;
     77 
     78     private final IAppContext mService;
     79     private final Handler mHandler;
     80     private final IAppContextListenerImpl mBinderListener;
     81     private final Map<Integer, AppContextOwnershipChangeListener> mOwnershipListeners;
     82 
     83     private AppContextChangeListener mListener;
     84     private int mContextFilter;
     85 
     86     /**
     87      * @hide
     88      */
     89     CarAppContextManager(IBinder service, Looper looper) {
     90         mService = IAppContext.Stub.asInterface(service);
     91         mHandler = new Handler(looper);
     92         mBinderListener = new IAppContextListenerImpl(this);
     93         mOwnershipListeners = new HashMap<Integer, AppContextOwnershipChangeListener>();
     94     }
     95 
     96     /**
     97      * Register listener to monitor app context change. Only one listener can be registered and
     98      * registering multiple times will lead into only the last listener to be active.
     99      * @param listener
    100      * @param contextFilter Flags of cotexts to get notification.
    101      * @throws CarNotConnectedException
    102      */
    103     public void registerContextListener(AppContextChangeListener listener, int contextFilter)
    104             throws CarNotConnectedException {
    105         if (listener == null) {
    106             throw new IllegalArgumentException("null listener");
    107         }
    108         synchronized(this) {
    109             if (mListener == null || mContextFilter != contextFilter) {
    110                 try {
    111                     mService.registerContextListener(mBinderListener, contextFilter);
    112                 } catch (RemoteException e) {
    113                     throw new CarNotConnectedException(e);
    114                 }
    115             }
    116             mListener = listener;
    117             mContextFilter = contextFilter;
    118         }
    119     }
    120 
    121     /**
    122      * Unregister listener and stop listening context change events. If app has owned a context
    123      * by {@link #setActiveContext(int)}, it will be reset to inactive state.
    124      * @throws CarNotConnectedException
    125      */
    126     public void unregisterContextListener() throws CarNotConnectedException {
    127         synchronized(this) {
    128             try {
    129                 mService.unregisterContextListener(mBinderListener);
    130             } catch (RemoteException e) {
    131                 throw new CarNotConnectedException(e);
    132             }
    133             mListener = null;
    134             mContextFilter = 0;
    135         }
    136     }
    137 
    138     public int getActiveAppContexts() throws CarNotConnectedException {
    139         try {
    140             return mService.getActiveAppContexts();
    141         } catch (RemoteException e) {
    142             throw new CarNotConnectedException(e);
    143         }
    144     }
    145 
    146     public boolean isOwningContext(int context) throws CarNotConnectedException {
    147         try {
    148             return mService.isOwningContext(mBinderListener, context);
    149         } catch (RemoteException e) {
    150             throw new CarNotConnectedException(e);
    151         }
    152     }
    153 
    154     /**
    155      * Set the given contexts as active. By setting this, the application is becoming owner
    156      * of the context, and will get
    157      * {@link AppContextOwnershipChangeListener#onAppContextOwnershipLoss(int)}
    158      * if ownership is given to other app by calling this. Fore-ground app will have higher priority
    159      * and other app cannot set the same context while owner is in fore-ground.
    160      * Only one listener per context can be registered and
    161      * registering multiple times will lead into only the last listener to be active.
    162      * @param ownershipListener
    163      * @param contexts
    164      * @throws CarNotConnectedException
    165      * @throws SecurityException If owner cannot be changed.
    166      */
    167     public void setActiveContexts(AppContextOwnershipChangeListener ownershipListener, int contexts)
    168             throws SecurityException, CarNotConnectedException {
    169         if (ownershipListener == null) {
    170             throw new IllegalArgumentException("null listener");
    171         }
    172         synchronized (this) {
    173             try {
    174                 mService.setActiveContexts(mBinderListener, contexts);
    175             } catch (RemoteException e) {
    176                 throw new CarNotConnectedException(e);
    177             }
    178             for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
    179                 if ((flag & contexts) != 0) {
    180                     mOwnershipListeners.put(flag, ownershipListener);
    181                 }
    182             }
    183         }
    184     }
    185 
    186     /**
    187      * Reset the given contexts, i.e. mark them as inactive. This also involves releasing ownership
    188      * for the context.
    189      * @param contexts
    190      * @throws CarNotConnectedException
    191      */
    192     public void resetActiveContexts(int contexts) throws CarNotConnectedException {
    193         try {
    194             mService.resetActiveContexts(mBinderListener, contexts);
    195         } catch (RemoteException e) {
    196             throw new CarNotConnectedException(e);
    197         }
    198         synchronized (this) {
    199             for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
    200                 if ((flag & contexts) != 0) {
    201                     mOwnershipListeners.remove(flag);
    202                 }
    203             }
    204         }
    205     }
    206 
    207     @Override
    208     public void onCarDisconnected() {
    209         // nothing to do
    210     }
    211 
    212     private void handleAppContextChange(int activeContexts) {
    213         AppContextChangeListener listener;
    214         int newContext;
    215         synchronized (this) {
    216             if (mListener == null) {
    217                 return;
    218             }
    219             listener = mListener;
    220             newContext = activeContexts & mContextFilter;
    221         }
    222         listener.onAppContextChange(newContext);
    223     }
    224 
    225     private void handleAppContextOwnershipLoss(int context) {
    226         AppContextOwnershipChangeListener listener;
    227         synchronized (this) {
    228             listener = mOwnershipListeners.get(context);
    229             if (listener == null) {
    230                 return;
    231             }
    232         }
    233         listener.onAppContextOwnershipLoss(context);
    234     }
    235 
    236     private static class IAppContextListenerImpl extends IAppContextListener.Stub {
    237 
    238         private final WeakReference<CarAppContextManager> mManager;
    239 
    240         private IAppContextListenerImpl(CarAppContextManager manager) {
    241             mManager = new WeakReference<>(manager);
    242         }
    243 
    244         @Override
    245         public void onAppContextChange(final int activeContexts) {
    246             final CarAppContextManager manager = mManager.get();
    247             if (manager == null) {
    248                 return;
    249             }
    250             manager.mHandler.post(new Runnable() {
    251                 @Override
    252                 public void run() {
    253                     manager.handleAppContextChange(activeContexts);
    254                 }
    255             });
    256         }
    257 
    258         @Override
    259         public void onAppContextOwnershipLoss(final int context) {
    260             final CarAppContextManager manager = mManager.get();
    261             if (manager == null) {
    262                 return;
    263             }
    264             manager.mHandler.post(new Runnable() {
    265                 @Override
    266                 public void run() {
    267                     manager.handleAppContextOwnershipLoss(context);
    268                 }
    269             });
    270         }
    271     }
    272 }
    273