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