• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Receiver(ICountryListener listener)51         public Receiver(ICountryListener listener) {
52             mListener = listener;
53             mKey = listener.asBinder();
54         }
55 
binderDied()56         public void binderDied() {
57             removeListener(mKey);
58         }
59 
60         @Override
equals(Object otherObj)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
hashCode()69         public int hashCode() {
70             return mKey.hashCode();
71         }
72 
getListener()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 
CountryDetectorService(Context context)87     public CountryDetectorService(Context context) {
88         super();
89         mReceivers = new HashMap<IBinder, Receiver>();
90         mContext = context;
91     }
92 
93     @Override
detectCountry()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
addCountryListener(ICountryListener listener)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
removeCountryListener(ICountryListener listener)116     public void removeCountryListener(ICountryListener listener) throws RemoteException {
117         if (!mSystemReady) {
118             throw new RemoteException();
119         }
120         removeListener(listener.asBinder());
121     }
122 
addListener(ICountryListener listener)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 
removeListener(IBinder key)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 
notifyReceivers(Country country)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 
systemReady()163     void systemReady() {
164         // Shall we wait for the initialization finish.
165         Thread thread = new Thread(this, "CountryDetectorService");
166         thread.start();
167     }
168 
initialize()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 
run()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 
setCountryListener(final CountryListener listener)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
isSystemReady()201     boolean isSystemReady() {
202         return mSystemReady;
203     }
204 }
205