Home | History | Annotate | Download | only in navigation
      1 /*
      2  * Copyright (C) 2016 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 package android.car.navigation;
     17 
     18 import android.car.CarApiUtil;
     19 import android.car.CarLibLog;
     20 import android.car.CarManagerBase;
     21 import android.car.CarNotConnectedException;
     22 import android.graphics.Bitmap;
     23 import android.os.Handler;
     24 import android.os.Handler.Callback;
     25 import android.os.IBinder;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 
     31 import java.lang.ref.WeakReference;
     32 
     33 /**
     34  * API for providing navigation status for instrument cluster.
     35  * @hide
     36  */
     37 public class CarNavigationManager implements CarManagerBase {
     38 
     39     /**
     40      * Listener navigation related events.
     41      * Callbacks are called in the Looper context.
     42      */
     43     public interface CarNavigationListener {
     44         /** Instrument Cluster started in navigation mode */
     45         void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster);
     46         /** Instrument cluster ended */
     47         void onInstrumentClusterStop();
     48     }
     49 
     50     /** Navigation status */
     51     public static final int STATUS_UNAVAILABLE = 0;
     52     public static final int STATUS_ACTIVE = 1;
     53     public static final int STATUS_INACTIVE = 2;
     54     /** Turn Types */
     55     public static final int TURN_UNKNOWN = 0;
     56     public static final int TURN_DEPART = 1;
     57     public static final int TURN_NAME_CHANGE = 2;
     58     public static final int TURN_SLIGHT_TURN = 3;
     59     public static final int TURN_TURN = 4;
     60     public static final int TURN_SHARP_TURN = 5;
     61     public static final int TURN_U_TURN = 6;
     62     public static final int TURN_ON_RAMP = 7;
     63     public static final int TURN_OFF_RAMP = 8;
     64     public static final int TURN_FORK = 9;
     65     public static final int TURN_MERGE = 10;
     66     public static final int TURN_ROUNDABOUT_ENTER = 11;
     67     public static final int TURN_ROUNDABOUT_EXIT = 12;
     68     public static final int TURN_ROUNDABOUT_ENTER_AND_EXIT = 13;
     69     public static final int TURN_STRAIGHT = 14;
     70     public static final int TURN_FERRY_BOAT = 16;
     71     public static final int TURN_FERRY_TRAIN = 17;
     72     public static final int TURN_DESTINATION = 19;
     73     /** Turn Side */
     74     public static final int TURN_SIDE_LEFT = 1;
     75     public static final int TURN_SIDE_RIGHT = 2;
     76     public static final int TURN_SIDE_UNSPECIFIED = 3;
     77 
     78     private static final int START = 1;
     79     private static final int STOP = 2;
     80 
     81     private static final String TAG = CarLibLog.TAG_NAV;
     82 
     83     private final ICarNavigation mService;
     84     private ICarNavigationEventListenerImpl mICarNavigationEventListenerImpl;
     85     private CarNavigationListener mCarNavigationListener;
     86     private CarNavigationInstrumentCluster mInstrumentCluster;
     87     private final Handler mHandler;
     88     private final Callback mHandlerCallback = new Callback() {
     89         @Override
     90         public boolean handleMessage(Message msg) {
     91             Log.d(TAG, "handleMessage, listener: " + mCarNavigationListener + ", msg.what: " +
     92                     msg.what);
     93             if (mCarNavigationListener != null) {
     94                 switch (msg.what) {
     95                     case START:
     96                         Log.d(TAG, "mCarNavigationListener.onInstrumentClusterStart()");
     97                         mCarNavigationListener.onInstrumentClusterStart(mInstrumentCluster);
     98                         break;
     99                     case STOP:
    100                         mCarNavigationListener.onInstrumentClusterStop();
    101                         break;
    102                 }
    103             }
    104             return true;
    105         }
    106     };
    107 
    108     /**
    109      * Only for CarServiceLoader
    110      * @hide
    111      */
    112     public CarNavigationManager(IBinder service, Looper looper) {
    113         mHandler = new Handler(looper, mHandlerCallback);
    114         mService = ICarNavigation.Stub.asInterface(service);
    115     }
    116 
    117     /**
    118      * @param status new instrument cluster navigation status.
    119      * @return true if successful.
    120      * @throws CarNotConnectedException
    121      */
    122     public boolean sendNavigationStatus(int status) throws CarNotConnectedException {
    123         try {
    124             mService.sendNavigationStatus(status);
    125         } catch (IllegalStateException e) {
    126             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
    127         } catch (RemoteException e) {
    128             handleCarServiceRemoteExceptionAndThrow(e);
    129             return false;
    130         }
    131         return true;
    132     }
    133 
    134     /**
    135      * Sends a Navigation Next Step event to the car.
    136      * <p>
    137      * Note: For an example of a roundabout: if a roundabout has 4 exits, spaced evenly, then the
    138      * first exit will have turnNumber=1, turnAngle=90; the second will have turnNumber=2,
    139      * turnAngle=180; the third will have turnNumber=3, turnAngle=270.  turnNumber and turnAngle are
    140      * counted in the direction of travel around the roundabout (clockwise for roads where the car
    141      * drives on the left-hand side of the road, such as Australia; anti-clockwise for roads where
    142      * the car drives on the right, such as the USA).
    143      *
    144      * @param event event type ({@link #TURN_TURN}, {@link #TURN_U_TURN},
    145      *        {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}, etc).
    146      * @param road Name of the road
    147      * @param turnAngle turn angle in degrees between the roundabout entry and exit (0..359).  Only
    148      *        used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}.  -1 if unused.
    149      * @param turnNumber turn number, counting around from the roundabout entry to the exit.  Only
    150      *        used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}.  -1 if unused.
    151      * @param image image to be shown in the instrument cluster.  Null if instrument
    152      *        cluster type doesn't support images.
    153      * @param turnSide turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or
    154      *        {@link #TURN_SIDE_UNSPECIFIED}).
    155      * @return true if successful.
    156      * @throws CarNotConnectedException
    157      *
    158      */
    159     public boolean sendNavigationTurnEvent(int event, String road, int turnAngle, int turnNumber,
    160             Bitmap image, int turnSide) throws CarNotConnectedException {
    161         try {
    162             mService.sendNavigationTurnEvent(event, road, turnAngle, turnNumber, image, turnSide);
    163         } catch (IllegalStateException e) {
    164             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
    165         } catch (RemoteException e) {
    166             handleCarServiceRemoteExceptionAndThrow(e);
    167             return false;
    168         }
    169         return true;
    170     }
    171 
    172     /**
    173      * Sends a Navigation Next Step Distance event to the car.
    174      *
    175      * @param distanceMeters Distance to next event in meters.
    176      * @param timeSeconds Time to next event in seconds.
    177      * @return true if successful.
    178      * @throws CarNotConnectedException
    179      */
    180     public boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds)
    181             throws CarNotConnectedException {
    182         try {
    183             mService.sendNavigationTurnDistanceEvent(distanceMeters, timeSeconds);
    184         } catch (IllegalStateException e) {
    185             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
    186         } catch (RemoteException e) {
    187             handleCarServiceRemoteExceptionAndThrow(e);
    188             return false;
    189         }
    190         return true;
    191     }
    192 
    193     public boolean isInstrumentClusterSupported() throws CarNotConnectedException {
    194         try {
    195             return mService.isInstrumentClusterSupported();
    196         } catch (IllegalStateException e) {
    197             CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
    198         } catch (RemoteException e) {
    199             handleCarServiceRemoteExceptionAndThrow(e);
    200         }
    201         return false;
    202     }
    203 
    204     @Override
    205     public void onCarDisconnected() {
    206         Log.d(TAG, "onCarDisconnected");
    207         unregisterListener();
    208     }
    209 
    210     /**
    211      * @param listener {@link CarNavigationListener} to be registered, replacing any existing
    212      *        listeners.
    213      * @throws CarNotConnectedException
    214      */
    215     public void registerListener(CarNavigationListener listener)
    216             throws CarNotConnectedException {
    217         mCarNavigationListener = listener;
    218         if (mICarNavigationEventListenerImpl == null) {
    219             mICarNavigationEventListenerImpl =
    220                     new ICarNavigationEventListenerImpl(this);
    221             try {
    222                 mService.registerEventListener(mICarNavigationEventListenerImpl);
    223             } catch (IllegalStateException e) {
    224                 CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
    225             } catch (RemoteException e) {
    226                 handleCarServiceRemoteExceptionAndThrow(e);
    227             }
    228         }
    229     }
    230 
    231     /**
    232      * Unregisters {@link CarNavigationListener}.
    233      */
    234     public void unregisterListener() {
    235         try {
    236             mService.unregisterEventListener(mICarNavigationEventListenerImpl);
    237         } catch (IllegalStateException e) {
    238             // ignore
    239         } catch (RemoteException e) {
    240             // do not throw exception as this can happen during tearing down.
    241             handleCarServiceRemoteException(e);
    242         }
    243         mCarNavigationListener = null;
    244     }
    245 
    246     private void handleCarServiceRemoteExceptionAndThrow(RemoteException e)
    247             throws CarNotConnectedException {
    248         handleCarServiceRemoteException(e);
    249         throw new CarNotConnectedException();
    250     }
    251 
    252     private void handleCarServiceRemoteException(RemoteException e) {
    253         Log.w(TAG, "RemoteException from car service:" + e.getMessage());
    254         // nothing to do for now
    255     }
    256 
    257     private void handleOnStart(CarNavigationInstrumentCluster instrumentCluster) {
    258         Log.d(TAG, "onStart(" + instrumentCluster + ")");
    259         mInstrumentCluster = new CarNavigationInstrumentCluster(instrumentCluster);
    260         mHandler.sendMessage(mHandler.obtainMessage(START));
    261     }
    262 
    263     private void handleOnStop() {
    264         mHandler.sendMessage(mHandler.obtainMessage(STOP));
    265     }
    266 
    267     private static class ICarNavigationEventListenerImpl extends ICarNavigationEventListener.Stub {
    268         private final WeakReference<CarNavigationManager> mManager;
    269 
    270         public ICarNavigationEventListenerImpl(CarNavigationManager manager) {
    271             mManager = new WeakReference<>(manager);
    272         }
    273 
    274         @Override
    275         public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentClusterInfo) {
    276             CarNavigationManager manager = mManager.get();
    277             if (manager != null) {
    278                 manager.handleOnStart(instrumentClusterInfo);
    279             }
    280         }
    281 
    282         @Override
    283         public void onInstrumentClusterStop() {
    284             CarNavigationManager manager = mManager.get();
    285             if (manager != null) {
    286                 manager.handleOnStop();
    287             }
    288         }
    289     }
    290 }
    291