1 /** 2 * Copyright (C) 2007 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 6 * of 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 14 * under the License. 15 */ 16 17 package com.google.android.googlelogin; 18 19 import com.google.android.googleapps.IGoogleLoginService; 20 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.ServiceConnection; 24 import android.os.RemoteException; 25 import android.os.IBinder; 26 import android.os.Looper; 27 28 import java.util.concurrent.locks.Condition; 29 import java.util.concurrent.locks.Lock; 30 import java.util.concurrent.locks.ReentrantLock; 31 32 /** 33 * A helper designed for use by services and background tasks. 34 * This class provides blocking calls that wrap the binding to the 35 * {@link IGoogleLoginService GoogleLoginService} and the various 36 * calls to it. (Be sure not to call these blocking methods 37 * from your main thread, though; you always need to create a separate 38 * worker thread for operations that may block.) 39 * <p> 40 * It's best to instantiate this class once 41 * and make calls on that instance when necessary. 42 * The helper does not unbind from the GoogleLoginService after 43 * each call. 44 * <p> 45 * When you are done with this object, call close() to unbind from the 46 * GoogleLoginService. 47 */ 48 public class GoogleLoginServiceBlockingHelper { 49 private static final String TAG = "GoogleLoginServiceBlockingHelper"; 50 51 private final Context mContext; 52 private volatile IGoogleLoginService mGoogleLoginService = null; 53 private Lock mGoogleLoginServiceLock = new ReentrantLock(); 54 private Condition mBindWaitCondition = mGoogleLoginServiceLock.newCondition(); 55 private ServiceConnection mServiceConnection; 56 57 private final int mMinDelaySecs; 58 private final int mMaxDelaySecs; 59 private final double mBackoffFactor; 60 private int mDelay; 61 62 /** 63 * Whether the Google login service we've bound to is the 64 * Google-provided service. This will be set after we get a callback on the 65 * service connection, so the value is only valid if 66 * {@link #mGoogleLoginService} is not null. 67 * <p> 68 * Locked with the {@link #mGoogleLoginServiceLock} also. 69 */ 70 private boolean mGlsVerified; 71 72 /** 73 * Initializes the helper. 74 * @param context the Context in which this helper is running 75 * @throws GoogleLoginServiceNotFoundException if the Google login service cannot be found. 76 */ GoogleLoginServiceBlockingHelper(Context context)77 public GoogleLoginServiceBlockingHelper(Context context) 78 throws GoogleLoginServiceNotFoundException { 79 mMinDelaySecs = 5; 80 mMaxDelaySecs = 5 * 60; // 5 minutes 81 mBackoffFactor = 2.0; 82 mDelay = mMinDelaySecs; 83 mContext = context; 84 85 // Ensure the Google Login Service is available 86 if (!GoogleAppsVerifier.isServiceAvailable(context, 87 GoogleLoginServiceConstants.FULLY_QUALIFIED_SERVICE_NAME)) { 88 throw new GoogleLoginServiceNotFoundException( 89 GoogleLoginServiceConstants.ERROR_CODE_GLS_NOT_FOUND); 90 } 91 92 mGoogleLoginServiceLock.lock(); 93 try { 94 mServiceConnection = new ServiceConnection() { 95 public void onServiceConnected(ComponentName className, IBinder service) { 96 try { 97 mGoogleLoginServiceLock.lock(); 98 99 /* 100 * Verify that the service we just connected to is 101 * provided by Google. Eventually, this will manifest 102 * into an exception, but we can't throw it here because 103 * our client isn't in the call stack right now. 104 */ 105 mGlsVerified = GoogleAppsVerifier.isGoogleAppsVerified(mContext); 106 107 mGoogleLoginService = IGoogleLoginService.Stub.asInterface(service); 108 109 mBindWaitCondition.signalAll(); 110 } finally { 111 mGoogleLoginServiceLock.unlock(); 112 } 113 } 114 115 public void onServiceDisconnected(ComponentName className) { 116 mGoogleLoginServiceLock.lock(); 117 mGoogleLoginService = null; 118 mGoogleLoginServiceLock.unlock(); 119 } 120 }; 121 122 if (!mContext.bindService(GoogleLoginServiceConstants.SERVICE_INTENT, 123 mServiceConnection, Context.BIND_AUTO_CREATE)) { 124 throw new GoogleLoginServiceNotFoundException( 125 GoogleLoginServiceConstants.ERROR_CODE_GLS_NOT_FOUND); 126 } 127 } finally { 128 mGoogleLoginServiceLock.unlock(); 129 } 130 } 131 132 /** 133 * Releases the binding to the GoogleLoginService (if one exists). This object 134 * is no longer usable one this method is invoked. 135 */ close()136 public void close() { 137 mGoogleLoginServiceLock.lock(); 138 try { 139 if (mServiceConnection != null) { 140 mContext.unbindService(mServiceConnection); 141 mServiceConnection = null; 142 mGoogleLoginService = null; 143 } 144 } finally { 145 mGoogleLoginServiceLock.unlock(); 146 } 147 } 148 149 /** 150 * Sleep for an exponentially-increasing length of time (bounded 151 * by mMinDelaySecs and mMaxDelaySecs). 152 */ delay()153 private void delay() { 154 try { 155 Thread.sleep(mDelay * 1000L); 156 } catch (InterruptedException ignore) { 157 // just delay for less time 158 } 159 mDelay *= mBackoffFactor; 160 if (mDelay > mMaxDelaySecs) mDelay = mMaxDelaySecs; 161 } 162 163 /** 164 * Reset the sleep time used by delay() to the minimum. 165 */ resetDelay()166 private void resetDelay() { 167 mDelay = mMinDelaySecs; 168 } 169 170 /** 171 * Waits for mGoogleLoginService to be nun-null and then returns it. It is set in the 172 * onServiceConnected that is called as a result of the bind that is called by the 173 * constructor. 174 * @return the GoogleLoginService, guaranteed to be non-null 175 * @throws GoogleLoginServiceNotFoundException if the Google login service cannot be found. 176 */ getLoginService()177 private IGoogleLoginService getLoginService() throws GoogleLoginServiceNotFoundException { 178 if (Looper.myLooper() == mContext.getMainLooper()) { 179 throw new IllegalStateException("calling GoogleLoginServiceBlockingHelper methods " 180 + "from your main thread can lead to deadlock"); 181 } 182 try { 183 mGoogleLoginServiceLock.lock(); 184 while (mGoogleLoginService == null) { 185 try { 186 mBindWaitCondition.await(); 187 } catch (InterruptedException e) { 188 // keep waiting 189 } 190 } 191 192 checkGoogleLoginServiceVerificationLocked(); 193 194 return mGoogleLoginService; 195 } finally { 196 mGoogleLoginServiceLock.unlock(); 197 } 198 } 199 checkGoogleLoginServiceVerificationLocked()200 private void checkGoogleLoginServiceVerificationLocked() 201 throws GoogleLoginServiceNotFoundException { 202 if (mGoogleLoginService != null && !mGlsVerified) { 203 throw new GoogleLoginServiceNotFoundException( 204 GoogleLoginServiceConstants.ERROR_CODE_GLS_VERIFICATION_FAILED); 205 } 206 } 207 208 /** 209 * Gets the login service via getLoginService, which may block, and then 210 * invokes getAndroidId on it. 211 * 212 * @see IGoogleLoginService#getAndroidId() 213 * @return the Android ID for this device (a 64-bit value unique to this 214 * device); 0 if the device is not registered with google or if the Android 215 * ID is otherwise unavailable. 216 * @throws GoogleLoginServiceNotFoundException if the Google login service cannot be found. 217 */ getAndroidId(Context context)218 public static long getAndroidId(Context context) throws GoogleLoginServiceNotFoundException { 219 GoogleLoginServiceBlockingHelper h = new GoogleLoginServiceBlockingHelper(context); 220 try { 221 return h.getAndroidId(); 222 } finally { 223 h.close(); 224 } 225 } 226 227 /** 228 * Gets the login service via getLoginService, which may block, and then 229 * invokes getAndroidId on it. 230 * 231 * @see IGoogleLoginService#getAndroidId() 232 * @return the Android ID for this device (a 64-bit value unique to this 233 * device); 0 if the device is not registered with google or if the Android 234 * ID is otherwise unavailable. 235 * @throws GoogleLoginServiceNotFoundException if the Google login service cannot be found. 236 */ getAndroidId()237 public long getAndroidId() throws GoogleLoginServiceNotFoundException { 238 resetDelay(); 239 while (true) { 240 IGoogleLoginService loginService = getLoginService(); 241 try { 242 return loginService.getAndroidId(); 243 } catch (RemoteException e) { 244 // the next call to getLoginService will wait until the service 245 // is reconnected 246 delay(); 247 } 248 } 249 } 250 } 251