Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2014 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.server.location;
     18 
     19 import com.android.internal.util.Preconditions;
     20 
     21 import android.annotation.NonNull;
     22 import android.os.Handler;
     23 import android.os.IBinder;
     24 import android.os.IInterface;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 
     28 import java.lang.Runnable;
     29 import java.util.HashMap;
     30 import java.util.Map;
     31 
     32 /**
     33  * A helper class, that handles operations in remote listeners, and tracks for remote process death.
     34  */
     35 abstract class RemoteListenerHelper<TListener extends IInterface> {
     36 
     37     protected static final int RESULT_SUCCESS = 0;
     38     protected static final int RESULT_NOT_AVAILABLE = 1;
     39     protected static final int RESULT_NOT_SUPPORTED = 2;
     40     protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
     41     protected static final int RESULT_INTERNAL_ERROR = 4;
     42     protected static final int RESULT_UNKNOWN = 5;
     43 
     44     private final Handler mHandler;
     45     private final String mTag;
     46 
     47     private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
     48 
     49     private boolean mIsRegistered;  // must access only on handler thread
     50     private boolean mHasIsSupported;
     51     private boolean mIsSupported;
     52 
     53     private int mLastReportedResult = RESULT_UNKNOWN;
     54 
     55     protected RemoteListenerHelper(Handler handler, String name) {
     56         Preconditions.checkNotNull(name);
     57         mHandler = handler;
     58         mTag = name;
     59     }
     60 
     61     public boolean addListener(@NonNull TListener listener) {
     62         Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
     63         IBinder binder = listener.asBinder();
     64         LinkedListener deathListener = new LinkedListener(listener);
     65         synchronized (mListenerMap) {
     66             if (mListenerMap.containsKey(binder)) {
     67                 // listener already added
     68                 return true;
     69             }
     70             try {
     71                 binder.linkToDeath(deathListener, 0 /* flags */);
     72             } catch (RemoteException e) {
     73                 // if the remote process registering the listener is already death, just swallow the
     74                 // exception and return
     75                 Log.v(mTag, "Remote listener already died.", e);
     76                 return false;
     77             }
     78             mListenerMap.put(binder, deathListener);
     79 
     80             // update statuses we already know about, starting from the ones that will never change
     81             int result;
     82             if (!isAvailableInPlatform()) {
     83                 result = RESULT_NOT_AVAILABLE;
     84             } else if (mHasIsSupported && !mIsSupported) {
     85                 result = RESULT_NOT_SUPPORTED;
     86             } else if (!isGpsEnabled()) {
     87                 // only attempt to register if GPS is enabled, otherwise we will register once GPS
     88                 // becomes available
     89                 result = RESULT_GPS_LOCATION_DISABLED;
     90             } else if (mHasIsSupported && mIsSupported) {
     91                 tryRegister();
     92                 // initially presume success, possible internal error could follow asynchornously
     93                 result = RESULT_SUCCESS;
     94             } else {
     95                 // at this point if the supported flag is not set, the notification will be sent
     96                 // asynchronously in the future
     97                 return true;
     98             }
     99             post(listener, getHandlerOperation(result));
    100         }
    101         return true;
    102     }
    103 
    104     public void removeListener(@NonNull TListener listener) {
    105         Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
    106         IBinder binder = listener.asBinder();
    107         LinkedListener linkedListener;
    108         synchronized (mListenerMap) {
    109             linkedListener = mListenerMap.remove(binder);
    110             if (mListenerMap.isEmpty()) {
    111                 tryUnregister();
    112             }
    113         }
    114         if (linkedListener != null) {
    115             binder.unlinkToDeath(linkedListener, 0 /* flags */);
    116         }
    117     }
    118 
    119     protected abstract boolean isAvailableInPlatform();
    120     protected abstract boolean isGpsEnabled();
    121     protected abstract boolean registerWithService(); // must access only on handler thread
    122     protected abstract void unregisterFromService(); // must access only on handler thread
    123     protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
    124 
    125     protected interface ListenerOperation<TListener extends IInterface> {
    126         void execute(TListener listener) throws RemoteException;
    127     }
    128 
    129     protected void foreach(ListenerOperation<TListener> operation) {
    130         synchronized (mListenerMap) {
    131             foreachUnsafe(operation);
    132         }
    133     }
    134 
    135     protected void setSupported(boolean value) {
    136         synchronized (mListenerMap) {
    137             mHasIsSupported = true;
    138             mIsSupported = value;
    139         }
    140     }
    141 
    142     protected void tryUpdateRegistrationWithService() {
    143         synchronized (mListenerMap) {
    144             if (!isGpsEnabled()) {
    145                 tryUnregister();
    146                 return;
    147             }
    148             if (mListenerMap.isEmpty()) {
    149                 return;
    150             }
    151             tryRegister();
    152         }
    153     }
    154 
    155     protected void updateResult() {
    156         synchronized (mListenerMap) {
    157             int newResult = calculateCurrentResultUnsafe();
    158             if (mLastReportedResult == newResult) {
    159                 return;
    160             }
    161             foreachUnsafe(getHandlerOperation(newResult));
    162             mLastReportedResult = newResult;
    163         }
    164     }
    165 
    166     private void foreachUnsafe(ListenerOperation<TListener> operation) {
    167         for (LinkedListener linkedListener : mListenerMap.values()) {
    168             post(linkedListener.getUnderlyingListener(), operation);
    169         }
    170     }
    171 
    172     private void post(TListener listener, ListenerOperation<TListener> operation) {
    173         if (operation != null) {
    174             mHandler.post(new HandlerRunnable(listener, operation));
    175         }
    176     }
    177 
    178     private void tryRegister() {
    179         mHandler.post(new Runnable() {
    180             @Override
    181             public void run() {
    182                 if (!mIsRegistered) {
    183                     mIsRegistered = registerWithService();
    184                 }
    185                 if (!mIsRegistered) {
    186                     // post back a failure
    187                     mHandler.post(new Runnable() {
    188                         @Override
    189                         public void run() {
    190                             synchronized (mListenerMap) {
    191                                 ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
    192                                 foreachUnsafe(operation);
    193                             }
    194                         }
    195                     });
    196                 }
    197             }
    198         });
    199     }
    200 
    201     private void tryUnregister() {
    202         mHandler.post(new Runnable() {
    203             @Override
    204             public void run() {
    205                 if (!mIsRegistered) {
    206                     return;
    207                 }
    208                 unregisterFromService();
    209                 mIsRegistered = false;
    210             }
    211         });
    212     }
    213 
    214     private int calculateCurrentResultUnsafe() {
    215         // update statuses we already know about, starting from the ones that will never change
    216         if (!isAvailableInPlatform()) {
    217             return RESULT_NOT_AVAILABLE;
    218         }
    219         if (!mHasIsSupported || mListenerMap.isEmpty()) {
    220             // we'll update once we have a supported status available
    221             return RESULT_UNKNOWN;
    222         }
    223         if (!mIsSupported) {
    224             return RESULT_NOT_SUPPORTED;
    225         }
    226         if (!isGpsEnabled()) {
    227             return RESULT_GPS_LOCATION_DISABLED;
    228         }
    229         return RESULT_SUCCESS;
    230     }
    231 
    232     private class LinkedListener implements IBinder.DeathRecipient {
    233         private final TListener mListener;
    234 
    235         public LinkedListener(@NonNull TListener listener) {
    236             mListener = listener;
    237         }
    238 
    239         @NonNull
    240         public TListener getUnderlyingListener() {
    241             return mListener;
    242         }
    243 
    244         @Override
    245         public void binderDied() {
    246             Log.d(mTag, "Remote Listener died: " + mListener);
    247             removeListener(mListener);
    248         }
    249     }
    250 
    251     private class HandlerRunnable implements Runnable {
    252         private final TListener mListener;
    253         private final ListenerOperation<TListener> mOperation;
    254 
    255         public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
    256             mListener = listener;
    257             mOperation = operation;
    258         }
    259 
    260         @Override
    261         public void run() {
    262             try {
    263                 mOperation.execute(mListener);
    264             } catch (RemoteException e) {
    265                 Log.v(mTag, "Error in monitored listener.", e);
    266             }
    267         }
    268     }
    269 }
    270