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.server.location.ComprehensiveCountryDetector; 25 26 import android.content.Context; 27 import android.location.Country; 28 import android.location.CountryListener; 29 import android.location.ICountryDetector; 30 import android.location.ICountryListener; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.util.PrintWriterPrinter; 35 import android.util.Printer; 36 import android.util.Slog; 37 38 /** 39 * This class detects the country that the user is in through 40 * {@link ComprehensiveCountryDetector}. 41 * 42 * @hide 43 */ 44 public 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() { 101 if (!mSystemReady) { 102 return null; // server not yet active 103 } else { 104 return mCountryDetector.detectCountry(); 105 } 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 systemRunning() { 171 // Shall we wait for the initialization finish. 172 BackgroundThread.getHandler().post(this); 173 } 174 175 private void initialize() { 176 mCountryDetector = new ComprehensiveCountryDetector(mContext); 177 mLocationBasedDetectorListener = new CountryListener() { 178 public void onCountryDetected(final Country country) { 179 mHandler.post(new Runnable() { 180 public void run() { 181 notifyReceivers(country); 182 } 183 }); 184 } 185 }; 186 } 187 188 public void run() { 189 mHandler = new Handler(); 190 initialize(); 191 mSystemReady = true; 192 } 193 194 protected void setCountryListener(final CountryListener listener) { 195 mHandler.post(new Runnable() { 196 @Override 197 public void run() { 198 mCountryDetector.setCountryListener(listener); 199 } 200 }); 201 } 202 203 // For testing 204 boolean isSystemReady() { 205 return mSystemReady; 206 } 207 208 @SuppressWarnings("unused") 209 @Override 210 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 211 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 212 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