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