• 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.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 
Receiver(ICountryListener listener)55         public Receiver(ICountryListener listener) {
56             mListener = listener;
57             mKey = listener.asBinder();
58         }
59 
binderDied()60         public void binderDied() {
61             removeListener(mKey);
62         }
63 
64         @Override
equals(Object otherObj)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
hashCode()73         public int hashCode() {
74             return mKey.hashCode();
75         }
76 
getListener()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 
CountryDetectorService(Context context)94     public CountryDetectorService(Context context) {
95         super();
96         mReceivers = new HashMap<IBinder, Receiver>();
97         mContext = context;
98     }
99 
100     @Override
detectCountry()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
addCountryListener(ICountryListener listener)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
removeCountryListener(ICountryListener listener)123     public void removeCountryListener(ICountryListener listener) throws RemoteException {
124         if (!mSystemReady) {
125             throw new RemoteException();
126         }
127         removeListener(listener.asBinder());
128     }
129 
addListener(ICountryListener listener)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 
removeListener(IBinder key)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 
notifyReceivers(Country country)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 
systemReady()170     void systemReady() {
171         // Shall we wait for the initialization finish.
172         Thread thread = new Thread(this, "CountryDetectorService");
173         thread.start();
174     }
175 
initialize()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 
run()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 
setCountryListener(final CountryListener listener)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
isSystemReady()208     boolean isSystemReady() {
209         return mSystemReady;
210     }
211 
212     @SuppressWarnings("unused")
213     @Override
dump(FileDescriptor fd, PrintWriter fout, String[] args)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