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.internal.util.DumpUtils;
     25 import com.android.server.location.ComprehensiveCountryDetector;
     26 
     27 import android.content.Context;
     28 import android.location.Country;
     29 import android.location.CountryListener;
     30 import android.location.ICountryDetector;
     31 import android.location.ICountryListener;
     32 import android.os.Handler;
     33 import android.os.IBinder;
     34 import android.os.RemoteException;
     35 import android.util.PrintWriterPrinter;
     36 import android.util.Printer;
     37 import android.util.Slog;
     38 
     39 /**
     40  * This class detects the country that the user is in through
     41  * {@link ComprehensiveCountryDetector}.
     42  *
     43  * @hide
     44  */
     45 public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
     46 
     47     /**
     48      * The class represents the remote listener, it will also removes itself
     49      * from listener list when the remote process was died.
     50      */
     51     private final class Receiver implements IBinder.DeathRecipient {
     52         private final ICountryListener mListener;
     53         private final IBinder mKey;
     54 
     55         public Receiver(ICountryListener listener) {
     56             mListener = listener;
     57             mKey = listener.asBinder();
     58         }
     59 
     60         public void binderDied() {
     61             removeListener(mKey);
     62         }
     63 
     64         @Override
     65         public boolean equals(Object otherObj) {
     66             if (otherObj instanceof Receiver) {
     67                 return mKey.equals(((Receiver) otherObj).mKey);
     68             }
     69             return false;
     70         }
     71 
     72         @Override
     73         public int hashCode() {
     74             return mKey.hashCode();
     75         }
     76 
     77         public ICountryListener getListener() {
     78             return mListener;
     79         }
     80     }
     81 
     82     private final static String TAG = "CountryDetector";
     83 
     84     /** Whether to dump the state of the country detector service to bugreports */
     85     private static final boolean DEBUG = false;
     86 
     87     private final HashMap<IBinder, Receiver> mReceivers;
     88     private final Context mContext;
     89     private ComprehensiveCountryDetector mCountryDetector;
     90     private boolean mSystemReady;
     91     private Handler mHandler;
     92     private CountryListener mLocationBasedDetectorListener;
     93 
     94     public CountryDetectorService(Context context) {
     95         super();
     96         mReceivers = new HashMap<IBinder, Receiver>();
     97         mContext = context;
     98     }
     99 
    100     @Override
    101     public Country detectCountry() {
    102         if (!mSystemReady) {
    103             return null;   // server not yet active
    104         } else {
    105             return mCountryDetector.detectCountry();
    106         }
    107     }
    108 
    109     /**
    110      * Add the ICountryListener into the listener list.
    111      */
    112     @Override
    113     public void addCountryListener(ICountryListener listener) throws RemoteException {
    114         if (!mSystemReady) {
    115             throw new RemoteException();
    116         }
    117         addListener(listener);
    118     }
    119 
    120     /**
    121      * Remove the ICountryListener from the listener list.
    122      */
    123     @Override
    124     public void removeCountryListener(ICountryListener listener) throws RemoteException {
    125         if (!mSystemReady) {
    126             throw new RemoteException();
    127         }
    128         removeListener(listener.asBinder());
    129     }
    130 
    131     private void addListener(ICountryListener listener) {
    132         synchronized (mReceivers) {
    133             Receiver r = new Receiver(listener);
    134             try {
    135                 listener.asBinder().linkToDeath(r, 0);
    136                 mReceivers.put(listener.asBinder(), r);
    137                 if (mReceivers.size() == 1) {
    138                     Slog.d(TAG, "The first listener is added");
    139                     setCountryListener(mLocationBasedDetectorListener);
    140                 }
    141             } catch (RemoteException e) {
    142                 Slog.e(TAG, "linkToDeath failed:", e);
    143             }
    144         }
    145     }
    146 
    147     private void removeListener(IBinder key) {
    148         synchronized (mReceivers) {
    149             mReceivers.remove(key);
    150             if (mReceivers.isEmpty()) {
    151                 setCountryListener(null);
    152                 Slog.d(TAG, "No listener is left");
    153             }
    154         }
    155     }
    156 
    157 
    158     protected void notifyReceivers(Country country) {
    159         synchronized(mReceivers) {
    160             for (Receiver receiver : mReceivers.values()) {
    161                 try {
    162                     receiver.getListener().onCountryDetected(country);
    163                 } catch (RemoteException e) {
    164                     // TODO: Shall we remove the receiver?
    165                     Slog.e(TAG, "notifyReceivers failed:", e);
    166                 }
    167             }
    168         }
    169     }
    170 
    171     void systemRunning() {
    172         // Shall we wait for the initialization finish.
    173         BackgroundThread.getHandler().post(this);
    174     }
    175 
    176     private void initialize() {
    177         mCountryDetector = new ComprehensiveCountryDetector(mContext);
    178         mLocationBasedDetectorListener = new CountryListener() {
    179             public void onCountryDetected(final Country country) {
    180                 mHandler.post(new Runnable() {
    181                     public void run() {
    182                         notifyReceivers(country);
    183                     }
    184                 });
    185             }
    186         };
    187     }
    188 
    189     public void run() {
    190         mHandler = new Handler();
    191         initialize();
    192         mSystemReady = true;
    193     }
    194 
    195     protected void setCountryListener(final CountryListener listener) {
    196         mHandler.post(new Runnable() {
    197             @Override
    198             public void run() {
    199                 mCountryDetector.setCountryListener(listener);
    200             }
    201         });
    202     }
    203 
    204     // For testing
    205     boolean isSystemReady() {
    206         return mSystemReady;
    207     }
    208 
    209     @SuppressWarnings("unused")
    210     @Override
    211     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
    212         if (!DumpUtils.checkDumpPermission(mContext, TAG, fout)) return;
    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