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.server.location.ComprehensiveCountryDetector;
     24 
     25 import android.content.Context;
     26 import android.location.Country;
     27 import android.location.CountryListener;
     28 import android.location.ICountryDetector;
     29 import android.location.ICountryListener;
     30 import android.os.Handler;
     31 import android.os.IBinder;
     32 import android.os.Looper;
     33 import android.os.Process;
     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() throws RemoteException {
    102         if (!mSystemReady) {
    103             throw new RemoteException();
    104         }
    105         return mCountryDetector.detectCountry();
    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 systemReady() {
    171         // Shall we wait for the initialization finish.
    172         Thread thread = new Thread(this, "CountryDetectorService");
    173         thread.start();
    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         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    191         Looper.prepare();
    192         mHandler = new Handler();
    193         initialize();
    194         mSystemReady = true;
    195         Looper.loop();
    196     }
    197 
    198     protected void setCountryListener(final CountryListener listener) {
    199         mHandler.post(new Runnable() {
    200             @Override
    201             public void run() {
    202                 mCountryDetector.setCountryListener(listener);
    203             }
    204         });
    205     }
    206 
    207     // For testing
    208     boolean isSystemReady() {
    209         return mSystemReady;
    210     }
    211 
    212     @SuppressWarnings("unused")
    213     @Override
    214     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
    215         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
    216 
    217         if (!DEBUG) return;
    218         try {
    219             final Printer p = new PrintWriterPrinter(fout);
    220             p.println("CountryDetectorService state:");
    221             p.println("  Number of listeners=" + mReceivers.keySet().size());
    222             if (mCountryDetector == null) {
    223                 p.println("  ComprehensiveCountryDetector not initialized");
    224             } else {
    225                 p.println("  " + mCountryDetector.toString());
    226             }
    227         } catch (Exception e) {
    228             Slog.e(TAG, "Failed to dump CountryDetectorService: ", e);
    229         }
    230     }
    231 }
    232