Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.server;
     18 
     19 import java.io.FileDescriptor;
     20 import java.io.PrintWriter;
     21 import java.util.HashMap;
     22 
     23 import com.android.internal.os.BackgroundThread;
     24 import com.android.server.location.ComprehensiveCountryDetector;
     25 
     26 import android.content.Context;
     27 import android.location.Country;
     28 import android.location.CountryListener;
     29 import android.location.ICountryDetector;
     30 import android.location.ICountryListener;
     31 import android.os.Handler;
     32 import android.os.IBinder;
     33 import android.os.RemoteException;
     34 import android.util.PrintWriterPrinter;
     35 import android.util.Printer;
     36 import android.util.Slog;
     37 
     38 /**
     39  * This class detects the country that the user is in through
     40  * {@link ComprehensiveCountryDetector}.
     41  *
     42  * @hide
     43  */
     44 public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
     45 
     46     /**
     47      * The class represents the remote listener, it will also removes itself
     48      * from listener list when the remote process was died.
     49      */
     50     private final class Receiver implements IBinder.DeathRecipient {
     51         private final ICountryListener mListener;
     52         private final IBinder mKey;
     53 
     54         public Receiver(ICountryListener listener) {
     55             mListener = listener;
     56             mKey = listener.asBinder();
     57         }
     58 
     59         public void binderDied() {
     60             removeListener(mKey);
     61         }
     62 
     63         @Override
     64         public boolean equals(Object otherObj) {
     65             if (otherObj instanceof Receiver) {
     66                 return mKey.equals(((Receiver) otherObj).mKey);
     67             }
     68             return false;
     69         }
     70 
     71         @Override
     72         public int hashCode() {
     73             return mKey.hashCode();
     74         }
     75 
     76         public ICountryListener getListener() {
     77             return mListener;
     78         }
     79     }
     80 
     81     private final static String TAG = "CountryDetector";
     82 
     83     /** Whether to dump the state of the country detector service to bugreports */
     84     private static final boolean DEBUG = false;
     85 
     86     private final HashMap<IBinder, Receiver> mReceivers;
     87     private final Context mContext;
     88     private ComprehensiveCountryDetector mCountryDetector;
     89     private boolean mSystemReady;
     90     private Handler mHandler;
     91     private CountryListener mLocationBasedDetectorListener;
     92 
     93     public CountryDetectorService(Context context) {
     94         super();
     95         mReceivers = new HashMap<IBinder, Receiver>();
     96         mContext = context;
     97     }
     98 
     99     @Override
    100     public Country detectCountry() {
    101         if (!mSystemReady) {
    102             return null;   // server not yet active
    103         } else {
    104             return mCountryDetector.detectCountry();
    105         }
    106     }
    107 
    108     /**
    109      * Add the ICountryListener into the listener list.
    110      */
    111     @Override
    112     public void addCountryListener(ICountryListener listener) throws RemoteException {
    113         if (!mSystemReady) {
    114             throw new RemoteException();
    115         }
    116         addListener(listener);
    117     }
    118 
    119     /**
    120      * Remove the ICountryListener from the listener list.
    121      */
    122     @Override
    123     public void removeCountryListener(ICountryListener listener) throws RemoteException {
    124         if (!mSystemReady) {
    125             throw new RemoteException();
    126         }
    127         removeListener(listener.asBinder());
    128     }
    129 
    130     private void addListener(ICountryListener listener) {
    131         synchronized (mReceivers) {
    132             Receiver r = new Receiver(listener);
    133             try {
    134                 listener.asBinder().linkToDeath(r, 0);
    135                 mReceivers.put(listener.asBinder(), r);
    136                 if (mReceivers.size() == 1) {
    137                     Slog.d(TAG, "The first listener is added");
    138                     setCountryListener(mLocationBasedDetectorListener);
    139                 }
    140             } catch (RemoteException e) {
    141                 Slog.e(TAG, "linkToDeath failed:", e);
    142             }
    143         }
    144     }
    145 
    146     private void removeListener(IBinder key) {
    147         synchronized (mReceivers) {
    148             mReceivers.remove(key);
    149             if (mReceivers.isEmpty()) {
    150                 setCountryListener(null);
    151                 Slog.d(TAG, "No listener is left");
    152             }
    153         }
    154     }
    155 
    156 
    157     protected void notifyReceivers(Country country) {
    158         synchronized(mReceivers) {
    159             for (Receiver receiver : mReceivers.values()) {
    160                 try {
    161                     receiver.getListener().onCountryDetected(country);
    162                 } catch (RemoteException e) {
    163                     // TODO: Shall we remove the receiver?
    164                     Slog.e(TAG, "notifyReceivers failed:", e);
    165                 }
    166             }
    167         }
    168     }
    169 
    170     void systemRunning() {
    171         // Shall we wait for the initialization finish.
    172         BackgroundThread.getHandler().post(this);
    173     }
    174 
    175     private void initialize() {
    176         mCountryDetector = new ComprehensiveCountryDetector(mContext);
    177         mLocationBasedDetectorListener = new CountryListener() {
    178             public void onCountryDetected(final Country country) {
    179                 mHandler.post(new Runnable() {
    180                     public void run() {
    181                         notifyReceivers(country);
    182                     }
    183                 });
    184             }
    185         };
    186     }
    187 
    188     public void run() {
    189         mHandler = new Handler();
    190         initialize();
    191         mSystemReady = true;
    192     }
    193 
    194     protected void setCountryListener(final CountryListener listener) {
    195         mHandler.post(new Runnable() {
    196             @Override
    197             public void run() {
    198                 mCountryDetector.setCountryListener(listener);
    199             }
    200         });
    201     }
    202 
    203     // For testing
    204     boolean isSystemReady() {
    205         return mSystemReady;
    206     }
    207 
    208     @SuppressWarnings("unused")
    209     @Override
    210     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
    211         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
    212 
    213         if (!DEBUG) return;
    214         try {
    215             final Printer p = new PrintWriterPrinter(fout);
    216             p.println("CountryDetectorService state:");
    217             p.println("  Number of listeners=" + mReceivers.keySet().size());
    218             if (mCountryDetector == null) {
    219                 p.println("  ComprehensiveCountryDetector not initialized");
    220             } else {
    221                 p.println("  " + mCountryDetector.toString());
    222             }
    223         } catch (Exception e) {
    224             Slog.e(TAG, "Failed to dump CountryDetectorService: ", e);
    225         }
    226     }
    227 }
    228