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.internal.os.BackgroundThread; 24 import com.android.internal.util.DumpUtils; 25 import com.android.server.location.ComprehensiveCountryDetector; 26 27 import android.content.Context; 28 import android.location.Country; 29 import android.location.CountryListener; 30 import android.location.ICountryDetector; 31 import android.location.ICountryListener; 32 import android.os.Handler; 33 import android.os.IBinder; 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() { 102 if (!mSystemReady) { 103 return null; // server not yet active 104 } else { 105 return mCountryDetector.detectCountry(); 106 } 107 } 108 109 /** 110 * Add the ICountryListener into the listener list. 111 */ 112 @Override 113 public void addCountryListener(ICountryListener listener) throws RemoteException { 114 if (!mSystemReady) { 115 throw new RemoteException(); 116 } 117 addListener(listener); 118 } 119 120 /** 121 * Remove the ICountryListener from the listener list. 122 */ 123 @Override 124 public void removeCountryListener(ICountryListener listener) throws RemoteException { 125 if (!mSystemReady) { 126 throw new RemoteException(); 127 } 128 removeListener(listener.asBinder()); 129 } 130 131 private void addListener(ICountryListener listener) { 132 synchronized (mReceivers) { 133 Receiver r = new Receiver(listener); 134 try { 135 listener.asBinder().linkToDeath(r, 0); 136 mReceivers.put(listener.asBinder(), r); 137 if (mReceivers.size() == 1) { 138 Slog.d(TAG, "The first listener is added"); 139 setCountryListener(mLocationBasedDetectorListener); 140 } 141 } catch (RemoteException e) { 142 Slog.e(TAG, "linkToDeath failed:", e); 143 } 144 } 145 } 146 147 private void removeListener(IBinder key) { 148 synchronized (mReceivers) { 149 mReceivers.remove(key); 150 if (mReceivers.isEmpty()) { 151 setCountryListener(null); 152 Slog.d(TAG, "No listener is left"); 153 } 154 } 155 } 156 157 158 protected void notifyReceivers(Country country) { 159 synchronized(mReceivers) { 160 for (Receiver receiver : mReceivers.values()) { 161 try { 162 receiver.getListener().onCountryDetected(country); 163 } catch (RemoteException e) { 164 // TODO: Shall we remove the receiver? 165 Slog.e(TAG, "notifyReceivers failed:", e); 166 } 167 } 168 } 169 } 170 171 void systemRunning() { 172 // Shall we wait for the initialization finish. 173 BackgroundThread.getHandler().post(this); 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 mHandler = new Handler(); 191 initialize(); 192 mSystemReady = true; 193 } 194 195 protected void setCountryListener(final CountryListener listener) { 196 mHandler.post(new Runnable() { 197 @Override 198 public void run() { 199 mCountryDetector.setCountryListener(listener); 200 } 201 }); 202 } 203 204 // For testing 205 boolean isSystemReady() { 206 return mSystemReady; 207 } 208 209 @SuppressWarnings("unused") 210 @Override 211 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 212 if (!DumpUtils.checkDumpPermission(mContext, TAG, fout)) return; 213 if (!DEBUG) return; 214 try { 215 final Printer p = new PrintWriterPrinter(fout); 216 p.println("CountryDetectorService state:"); 217 p.println(" Number of listeners=" + mReceivers.keySet().size()); 218 if (mCountryDetector == null) { 219 p.println(" ComprehensiveCountryDetector not initialized"); 220 } else { 221 p.println(" " + mCountryDetector.toString()); 222 } 223 } catch (Exception e) { 224 Slog.e(TAG, "Failed to dump CountryDetectorService: ", e); 225 } 226 } 227 } 228