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