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