blob: 3081ebefd05a729555e6e14367a6c8a5519dfeea [file] [log] [blame]
Bai Taoa58a8752010-07-13 15:32:16 +08001/*
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
17package com.android.server;
18
19import java.util.HashMap;
20
21import com.android.server.location.ComprehensiveCountryDetector;
22
23import android.content.Context;
24import android.location.Country;
25import android.location.CountryListener;
26import android.location.ICountryDetector;
27import android.location.ICountryListener;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Process;
32import android.os.RemoteException;
33import android.util.Slog;
34
35/**
36 * This class detects the country that the user is in through
37 * {@link ComprehensiveCountryDetector}.
38 *
39 * @hide
40 */
41public 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}