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