• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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