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 55 public Receiver(ICountryListener listener) { 56 mListener = listener; 57 mKey = listener.asBinder(); 58 } 59 60 public void binderDied() { 61 removeListener(mKey); 62 } 63 64 @Override 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 73 public int hashCode() { 74 return mKey.hashCode(); 75 } 76 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 94 public CountryDetectorService(Context context) { 95 super(); 96 mReceivers = new HashMap<IBinder, Receiver>(); 97 mContext = context; 98 } 99 100 @Override 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 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 123 public void removeCountryListener(ICountryListener listener) throws RemoteException { 124 if (!mSystemReady) { 125 throw new RemoteException(); 126 } 127 removeListener(listener.asBinder()); 128 } 129 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 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 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 170 void systemReady() { 171 // Shall we wait for the initialization finish. 172 Thread thread = new Thread(this, "CountryDetectorService"); 173 thread.start(); 174 } 175 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 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 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 208 boolean isSystemReady() { 209 return mSystemReady; 210 } 211 212 @SuppressWarnings("unused") 213 @Override 214 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 215 if (!DEBUG) return; 216 try { 217 final Printer p = new PrintWriterPrinter(fout); 218 p.println("CountryDetectorService state:"); 219 p.println(" Number of listeners=" + mReceivers.keySet().size()); 220 if (mCountryDetector == null) { 221 p.println(" ComprehensiveCountryDetector not initialized"); 222 } else { 223 p.println(" " + mCountryDetector.toString()); 224 } 225 } catch (Exception e) { 226 Slog.e(TAG, "Failed to dump CountryDetectorService: ", e); 227 } 228 } 229 } 230