| OLD | NEW | 
|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 package org.chromium.chrome.browser.omaha; | 5 package org.chromium.chrome.browser.omaha; | 
| 6 | 6 | 
| 7 import android.app.IntentService; | 7 import android.app.IntentService; | 
| 8 import android.content.Context; | 8 import android.content.Context; | 
| 9 import android.content.Intent; | 9 import android.content.Intent; | 
| 10 import android.content.SharedPreferences; |  | 
| 11 import android.os.Build; |  | 
| 12 import android.support.annotation.IntDef; |  | 
| 13 |  | 
| 14 import org.chromium.base.Log; |  | 
| 15 import org.chromium.base.ThreadUtils; |  | 
| 16 import org.chromium.base.VisibleForTesting; |  | 
| 17 |  | 
| 18 import java.io.IOException; |  | 
| 19 import java.lang.annotation.Retention; |  | 
| 20 import java.lang.annotation.RetentionPolicy; |  | 
| 21 import java.net.HttpURLConnection; |  | 
| 22 import java.net.MalformedURLException; |  | 
| 23 import java.net.URL; |  | 
| 24 import java.util.concurrent.TimeUnit; |  | 
| 25 | 10 | 
| 26 /** | 11 /** | 
| 27  * Keeps tabs on the current state of Chrome, tracking if and when a request sho
     uld be sent to the | 12  * Runs the {@link OmahaBase} pipeline as a {@link IntentService}. | 
| 28  * Omaha Server. |  | 
| 29  * |  | 
| 30  * A hook in ChromeActivity's doDeferredResume() initializes the service.  Furth
     er attempts to |  | 
| 31  * reschedule events will be scheduled by the class itself. |  | 
| 32  * |  | 
| 33  * Each request to the server will perform an update check and ping the server. |  | 
| 34  * We use a repeating alarm to schedule the XML requests to be generated 5 hours
      apart. |  | 
| 35  * If Chrome isn't running when the alarm is fired, the request generation will 
     be stalled until |  | 
| 36  * the next time Chrome runs. |  | 
| 37  * |  | 
| 38  * mevissen suggested being conservative with our timers for sending requests. |  | 
| 39  * POST attempts that fail to be acknowledged by the server are re-attempted, wi
     th at least |  | 
| 40  * one hour between each attempt. |  | 
| 41  * |  | 
| 42  * Status is saved directly to the the disk after every operation.  Unit tests t
     esting the code |  | 
| 43  * paths without using Intents may need to call restoreState() manually as it is
      not automatically |  | 
| 44  * handled in onCreate(). |  | 
| 45  * |  | 
| 46  * Implementation notes: |  | 
| 47  * http://docs.google.com/a/google.com/document/d/1scTCovqASf5ktkOeVj8wFRkWTCeDY
     w2LrOBNn05CDB0/edit |  | 
| 48  * | 13  * | 
| 49  * NOTE: This class can never be renamed because the user may have Intents float
     ing around that | 14  * NOTE: This class can never be renamed because the user may have Intents float
     ing around that | 
| 50  *       reference this class specifically. | 15  *       reference this class specifically. | 
| 51  */ | 16  */ | 
| 52 public class OmahaClient extends IntentService { | 17 public class OmahaClient extends IntentService { | 
| 53     // Results of {@link #handlePostRequest()}. |  | 
| 54     @Retention(RetentionPolicy.SOURCE) |  | 
| 55     @IntDef({POST_RESULT_NO_REQUEST, POST_RESULT_SENT, POST_RESULT_FAILED, POST_
     RESULT_SCHEDULED}) |  | 
| 56     @interface PostResult {} |  | 
| 57     static final int POST_RESULT_NO_REQUEST = 0; |  | 
| 58     static final int POST_RESULT_SENT = 1; |  | 
| 59     static final int POST_RESULT_FAILED = 2; |  | 
| 60     static final int POST_RESULT_SCHEDULED = 3; |  | 
| 61 |  | 
| 62     private static final String TAG = "omaha"; | 18     private static final String TAG = "omaha"; | 
| 63 | 19 | 
| 64     /** Deprecated; kept around to cancel alarms set for OmahaClient pre-M58. */ |  | 
| 65     private static final String ACTION_REGISTER_REQUEST = |  | 
| 66             "org.chromium.chrome.browser.omaha.ACTION_REGISTER_REQUEST"; |  | 
| 67 |  | 
| 68     // Delays between events. |  | 
| 69     static final long MS_POST_BASE_DELAY = TimeUnit.HOURS.toMillis(1); |  | 
| 70     static final long MS_POST_MAX_DELAY = TimeUnit.HOURS.toMillis(5); |  | 
| 71     static final long MS_BETWEEN_REQUESTS = TimeUnit.HOURS.toMillis(5); |  | 
| 72     static final int MS_CONNECTION_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(1); |  | 
| 73 |  | 
| 74     // Strings indicating how the Chrome APK arrived on the user's device. These
      values MUST NOT |  | 
| 75     // be changed without updating the corresponding Omaha server strings. |  | 
| 76     static final String INSTALL_SOURCE_SYSTEM = "system_image"; |  | 
| 77     static final String INSTALL_SOURCE_ORGANIC = "organic"; |  | 
| 78 |  | 
| 79     private static final long INVALID_TIMESTAMP = -1; |  | 
| 80     @VisibleForTesting |  | 
| 81     static final String INVALID_REQUEST_ID = "invalid"; |  | 
| 82 |  | 
| 83     // Member fields not persisted to disk. |  | 
| 84     private boolean mStateHasBeenRestored; |  | 
| 85     private OmahaDelegate mDelegate; |  | 
| 86 |  | 
| 87     // State saved written to and read from disk. |  | 
| 88     private RequestData mCurrentRequest; |  | 
| 89     private long mTimestampOfInstall; |  | 
| 90     private long mTimestampForNextPostAttempt; |  | 
| 91     private long mTimestampForNewRequest; |  | 
| 92     private String mLatestVersion; |  | 
| 93     private String mMarketURL; |  | 
| 94     private String mInstallSource; |  | 
| 95     protected boolean mSendInstallEvent; |  | 
| 96 |  | 
| 97     public OmahaClient() { | 20     public OmahaClient() { | 
| 98         super(TAG); | 21         super(TAG); | 
| 99         setIntentRedelivery(true); | 22         setIntentRedelivery(true); | 
| 100     } | 23     } | 
| 101 | 24 | 
| 102     /** |  | 
| 103      * Handles an action on a thread separate from the UI thread. |  | 
| 104      * @param intent Intent fired by some part of Chrome. |  | 
| 105      */ |  | 
| 106     @Override | 25     @Override | 
| 107     public void onHandleIntent(Intent intent) { | 26     public void onHandleIntent(Intent intent) { | 
| 108         assert !ThreadUtils.runningOnUiThread(); | 27         OmahaService.getInstance(this).run(); | 
| 109         run(); |  | 
| 110     } | 28     } | 
| 111 | 29 | 
| 112     protected void run() { | 30     static Intent createIntent(Context context) { | 
| 113         if (mDelegate == null) mDelegate = new OmahaDelegateImpl(this); |  | 
| 114 |  | 
| 115         if (OmahaBase.isDisabled() || Build.VERSION.SDK_INT > Build.VERSION_CODE
     S.N |  | 
| 116                 || getRequestGenerator() == null) { |  | 
| 117             Log.v(TAG, "Disabled.  Ignoring intent."); |  | 
| 118             return; |  | 
| 119         } |  | 
| 120 |  | 
| 121         restoreState(getContext()); |  | 
| 122 |  | 
| 123         long nextTimestamp = Long.MAX_VALUE; |  | 
| 124         if (mDelegate.isChromeBeingUsed()) { |  | 
| 125             handleRegisterActiveRequest(); |  | 
| 126             nextTimestamp = Math.min(nextTimestamp, mTimestampForNewRequest); |  | 
| 127         } |  | 
| 128 |  | 
| 129         if (hasRequest()) { |  | 
| 130             int result = handlePostRequest(); |  | 
| 131             if (result == POST_RESULT_FAILED || result == POST_RESULT_SCHEDULED)
      { |  | 
| 132                 nextTimestamp = Math.min(nextTimestamp, mTimestampForNextPostAtt
     empt); |  | 
| 133             } |  | 
| 134         } |  | 
| 135 |  | 
| 136         // TODO(dfalcantara): Prevent Omaha code from repeatedly rescheduling it
     self immediately in |  | 
| 137         //                    case a scheduling error occurs. |  | 
| 138         if (nextTimestamp != Long.MAX_VALUE && nextTimestamp >= 0) { |  | 
| 139             mDelegate.scheduleService(this, nextTimestamp); |  | 
| 140         } |  | 
| 141         saveState(getContext()); |  | 
| 142     } |  | 
| 143 |  | 
| 144     /** |  | 
| 145      * Begin communicating with the Omaha Update Server. |  | 
| 146      */ |  | 
| 147     static void startService(Context context) { |  | 
| 148         context.startService(createOmahaIntent(context)); |  | 
| 149     } |  | 
| 150 |  | 
| 151     static Intent createOmahaIntent(Context context) { |  | 
| 152         return new Intent(context, OmahaClient.class); | 31         return new Intent(context, OmahaClient.class); | 
| 153     } | 32     } | 
| 154 |  | 
| 155     /** |  | 
| 156      * Determines if a new request should be generated.  New requests are only g
     enerated if enough |  | 
| 157      * time has passed between now and the last time a request was generated. |  | 
| 158      */ |  | 
| 159     private void handleRegisterActiveRequest() { |  | 
| 160         // If the current request is too old, generate a new one. |  | 
| 161         long currentTimestamp = getBackoffScheduler().getCurrentTime(); |  | 
| 162         boolean isTooOld = hasRequest() |  | 
| 163                 && mCurrentRequest.getAgeInMilliseconds(currentTimestamp) >= MS_
     BETWEEN_REQUESTS; |  | 
| 164         boolean isOverdue = currentTimestamp >= mTimestampForNewRequest; |  | 
| 165         if (isTooOld || isOverdue) { |  | 
| 166             registerNewRequest(currentTimestamp); |  | 
| 167         } |  | 
| 168     } |  | 
| 169 |  | 
| 170     /** |  | 
| 171      * Sends the request it is holding. |  | 
| 172      */ |  | 
| 173     private int handlePostRequest() { |  | 
| 174         if (!hasRequest()) { |  | 
| 175             mDelegate.onHandlePostRequestDone(POST_RESULT_NO_REQUEST, false); |  | 
| 176             return POST_RESULT_NO_REQUEST; |  | 
| 177         } |  | 
| 178 |  | 
| 179         // If enough time has passed since the last attempt, try sending a reque
     st. |  | 
| 180         int result; |  | 
| 181         long currentTimestamp = getBackoffScheduler().getCurrentTime(); |  | 
| 182         boolean installEventWasSent = false; |  | 
| 183         if (currentTimestamp >= mTimestampForNextPostAttempt) { |  | 
| 184             // All requests made during the same session should have the same ID
     . |  | 
| 185             String sessionID = mDelegate.generateUUID(); |  | 
| 186             boolean sendingInstallRequest = mSendInstallEvent; |  | 
| 187             boolean succeeded = generateAndPostRequest(currentTimestamp, session
     ID); |  | 
| 188 |  | 
| 189             if (succeeded && sendingInstallRequest) { |  | 
| 190                 // Only the first request ever generated should contain an insta
     ll event. |  | 
| 191                 mSendInstallEvent = false; |  | 
| 192                 installEventWasSent = true; |  | 
| 193 |  | 
| 194                 // Create and immediately send another request for a ping and up
     date check. |  | 
| 195                 registerNewRequest(currentTimestamp); |  | 
| 196                 succeeded &= generateAndPostRequest(currentTimestamp, sessionID)
     ; |  | 
| 197             } |  | 
| 198 |  | 
| 199             result = succeeded ? POST_RESULT_SENT : POST_RESULT_FAILED; |  | 
| 200         } else { |  | 
| 201             result = POST_RESULT_SCHEDULED; |  | 
| 202         } |  | 
| 203 |  | 
| 204         mDelegate.onHandlePostRequestDone(result, installEventWasSent); |  | 
| 205         return result; |  | 
| 206     } |  | 
| 207 |  | 
| 208     private boolean generateAndPostRequest(long currentTimestamp, String session
     ID) { |  | 
| 209         ExponentialBackoffScheduler scheduler = getBackoffScheduler(); |  | 
| 210         boolean succeeded = false; |  | 
| 211         try { |  | 
| 212             // Generate the XML for the current request. |  | 
| 213             long installAgeInDays = RequestGenerator.installAge(currentTimestamp
     , |  | 
| 214                     mTimestampOfInstall, mCurrentRequest.isSendInstallEvent()); |  | 
| 215             String version = VersionNumberGetter.getInstance().getCurrentlyUsedV
     ersion(this); |  | 
| 216             String xml = getRequestGenerator().generateXML( |  | 
| 217                     sessionID, version, installAgeInDays, mCurrentRequest); |  | 
| 218 |  | 
| 219             // Send the request to the server & wait for a response. |  | 
| 220             String response = postRequest(currentTimestamp, xml); |  | 
| 221 |  | 
| 222             // Parse out the response. |  | 
| 223             String appId = getRequestGenerator().getAppId(); |  | 
| 224             boolean sentPingAndUpdate = !mSendInstallEvent; |  | 
| 225             ResponseParser parser = new ResponseParser( |  | 
| 226                     appId, mSendInstallEvent, sentPingAndUpdate, sentPingAndUpda
     te); |  | 
| 227             parser.parseResponse(response); |  | 
| 228             mLatestVersion = parser.getNewVersion(); |  | 
| 229             mMarketURL = parser.getURL(); |  | 
| 230 |  | 
| 231             succeeded = true; |  | 
| 232         } catch (RequestFailureException e) { |  | 
| 233             Log.e(TAG, "Failed to contact server: ", e); |  | 
| 234         } |  | 
| 235 |  | 
| 236         if (succeeded) { |  | 
| 237             // If we've gotten this far, we've successfully sent a request. |  | 
| 238             mCurrentRequest = null; |  | 
| 239 |  | 
| 240             scheduler.resetFailedAttempts(); |  | 
| 241             mTimestampForNewRequest = scheduler.getCurrentTime() + MS_BETWEEN_RE
     QUESTS; |  | 
| 242             mTimestampForNextPostAttempt = scheduler.calculateNextTimestamp(); |  | 
| 243             Log.i(TAG, "Request to Server Successful. Timestamp for next request
     :" |  | 
| 244                     + String.valueOf(mTimestampForNextPostAttempt)); |  | 
| 245         } else { |  | 
| 246             // Set the alarm to try again later.  Failures are incremented after
      setting the timer |  | 
| 247             // to allow the first failure to incur the minimum base delay betwee
     n POSTs. |  | 
| 248             mTimestampForNextPostAttempt = scheduler.calculateNextTimestamp(); |  | 
| 249             scheduler.increaseFailedAttempts(); |  | 
| 250         } |  | 
| 251 |  | 
| 252         mDelegate.onGenerateAndPostRequestDone(succeeded); |  | 
| 253         return succeeded; |  | 
| 254     } |  | 
| 255 |  | 
| 256     /** |  | 
| 257      * Registers a new request with the current timestamp.  Internal timestamps 
     are reset to start |  | 
| 258      * fresh. |  | 
| 259      * @param currentTimestamp Current time. |  | 
| 260      */ |  | 
| 261     private void registerNewRequest(long currentTimestamp) { |  | 
| 262         mCurrentRequest = createRequestData(currentTimestamp, null); |  | 
| 263         getBackoffScheduler().resetFailedAttempts(); |  | 
| 264         mTimestampForNextPostAttempt = currentTimestamp; |  | 
| 265 |  | 
| 266         // Tentatively set the timestamp for a new request.  This will be update
     d when the server |  | 
| 267         // is successfully contacted. |  | 
| 268         mTimestampForNewRequest = currentTimestamp + MS_BETWEEN_REQUESTS; |  | 
| 269 |  | 
| 270         mDelegate.onRegisterNewRequestDone(mTimestampForNewRequest, mTimestampFo
     rNextPostAttempt); |  | 
| 271     } |  | 
| 272 |  | 
| 273     private RequestData createRequestData(long currentTimestamp, String persiste
     dID) { |  | 
| 274         // If we're sending a persisted event, keep trying to send the same requ
     est ID. |  | 
| 275         String requestID; |  | 
| 276         if (persistedID == null || INVALID_REQUEST_ID.equals(persistedID)) { |  | 
| 277             requestID = mDelegate.generateUUID(); |  | 
| 278         } else { |  | 
| 279             requestID = persistedID; |  | 
| 280         } |  | 
| 281         return new RequestData(mSendInstallEvent, currentTimestamp, requestID, m
     InstallSource); |  | 
| 282     } |  | 
| 283 |  | 
| 284     private boolean hasRequest() { |  | 
| 285         return mCurrentRequest != null; |  | 
| 286     } |  | 
| 287 |  | 
| 288     /** |  | 
| 289      * Posts the request to the Omaha server. |  | 
| 290      * @return the XML response as a String. |  | 
| 291      * @throws RequestFailureException if the request fails. |  | 
| 292      */ |  | 
| 293     private String postRequest(long timestamp, String xml) throws RequestFailure
     Exception { |  | 
| 294         String response = null; |  | 
| 295 |  | 
| 296         HttpURLConnection urlConnection = null; |  | 
| 297         try { |  | 
| 298             urlConnection = createConnection(); |  | 
| 299 |  | 
| 300             // Prepare the HTTP header. |  | 
| 301             urlConnection.setDoOutput(true); |  | 
| 302             urlConnection.setFixedLengthStreamingMode(xml.getBytes().length); |  | 
| 303             if (mSendInstallEvent && getBackoffScheduler().getNumFailedAttempts(
     ) > 0) { |  | 
| 304                 String age = Long.toString(mCurrentRequest.getAgeInSeconds(times
     tamp)); |  | 
| 305                 urlConnection.addRequestProperty("X-RequestAge", age); |  | 
| 306             } |  | 
| 307 |  | 
| 308             response = OmahaBase.sendRequestToServer(urlConnection, xml); |  | 
| 309         } catch (IllegalAccessError e) { |  | 
| 310             throw new RequestFailureException("Caught an IllegalAccessError:", e
     ); |  | 
| 311         } catch (IllegalArgumentException e) { |  | 
| 312             throw new RequestFailureException("Caught an IllegalArgumentExceptio
     n:", e); |  | 
| 313         } catch (IllegalStateException e) { |  | 
| 314             throw new RequestFailureException("Caught an IllegalStateException:"
     , e); |  | 
| 315         } finally { |  | 
| 316             if (urlConnection != null) { |  | 
| 317                 urlConnection.disconnect(); |  | 
| 318             } |  | 
| 319         } |  | 
| 320 |  | 
| 321         return response; |  | 
| 322     } |  | 
| 323 |  | 
| 324     /** |  | 
| 325      * Returns a HttpURLConnection to the server. |  | 
| 326      */ |  | 
| 327     @VisibleForTesting |  | 
| 328     protected HttpURLConnection createConnection() throws RequestFailureExceptio
     n { |  | 
| 329         try { |  | 
| 330             URL url = new URL(getRequestGenerator().getServerUrl()); |  | 
| 331             HttpURLConnection connection = (HttpURLConnection) url.openConnectio
     n(); |  | 
| 332             connection.setConnectTimeout(MS_CONNECTION_TIMEOUT); |  | 
| 333             connection.setReadTimeout(MS_CONNECTION_TIMEOUT); |  | 
| 334             return connection; |  | 
| 335         } catch (MalformedURLException e) { |  | 
| 336             throw new RequestFailureException("Caught a malformed URL exception.
     ", e); |  | 
| 337         } catch (IOException e) { |  | 
| 338             throw new RequestFailureException("Failed to open connection to URL"
     , e); |  | 
| 339         } |  | 
| 340     } |  | 
| 341 |  | 
| 342     /** |  | 
| 343      * Reads the data back from the file it was saved to.  Uses SharedPreference
     s to handle I/O. |  | 
| 344      * Sanity checks are performed on the timestamps to guard against clock chan
     ging. |  | 
| 345      */ |  | 
| 346     @VisibleForTesting |  | 
| 347     void restoreState(Context context) { |  | 
| 348         if (mStateHasBeenRestored) return; |  | 
| 349 |  | 
| 350         String installSource = |  | 
| 351                 mDelegate.isInSystemImage() ? INSTALL_SOURCE_SYSTEM : INSTALL_SO
     URCE_ORGANIC; |  | 
| 352         ExponentialBackoffScheduler scheduler = getBackoffScheduler(); |  | 
| 353         long currentTime = scheduler.getCurrentTime(); |  | 
| 354 |  | 
| 355         SharedPreferences preferences = OmahaBase.getSharedPreferences(context); |  | 
| 356         mTimestampForNewRequest = |  | 
| 357                 preferences.getLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, cu
     rrentTime); |  | 
| 358         mTimestampForNextPostAttempt = |  | 
| 359                 preferences.getLong(OmahaBase.PREF_TIMESTAMP_FOR_NEXT_POST_ATTEM
     PT, currentTime); |  | 
| 360         mTimestampOfInstall = preferences.getLong(OmahaBase.PREF_TIMESTAMP_OF_IN
     STALL, currentTime); |  | 
| 361         mSendInstallEvent = preferences.getBoolean(OmahaBase.PREF_SEND_INSTALL_E
     VENT, true); |  | 
| 362         mInstallSource = preferences.getString(OmahaBase.PREF_INSTALL_SOURCE, in
     stallSource); |  | 
| 363         mLatestVersion = preferences.getString(OmahaBase.PREF_LATEST_VERSION, ""
     ); |  | 
| 364         mMarketURL = preferences.getString(OmahaBase.PREF_MARKET_URL, ""); |  | 
| 365 |  | 
| 366         // If we're not sending an install event, don't bother restoring the req
     uest ID: |  | 
| 367         // the server does not expect to have persisted request IDs for pings or
      update checks. |  | 
| 368         String persistedRequestId = mSendInstallEvent |  | 
| 369                 ? preferences.getString(OmahaBase.PREF_PERSISTED_REQUEST_ID, INV
     ALID_REQUEST_ID) |  | 
| 370                 : INVALID_REQUEST_ID; |  | 
| 371         long requestTimestamp = |  | 
| 372                 preferences.getLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, INVALID
     _TIMESTAMP); |  | 
| 373         mCurrentRequest = requestTimestamp == INVALID_TIMESTAMP |  | 
| 374                 ? null : createRequestData(requestTimestamp, persistedRequestId)
     ; |  | 
| 375 |  | 
| 376         // Confirm that the timestamp for the next request is less than the base
      delay. |  | 
| 377         long delayToNewRequest = mTimestampForNewRequest - currentTime; |  | 
| 378         if (delayToNewRequest > MS_BETWEEN_REQUESTS) { |  | 
| 379             Log.w(TAG, "Delay to next request (" + delayToNewRequest |  | 
| 380                     + ") is longer than expected.  Resetting to now."); |  | 
| 381             mTimestampForNewRequest = currentTime; |  | 
| 382         } |  | 
| 383 |  | 
| 384         // Confirm that the timestamp for the next POST is less than the current
      delay. |  | 
| 385         long delayToNextPost = mTimestampForNextPostAttempt - currentTime; |  | 
| 386         long lastGeneratedDelay = scheduler.getGeneratedDelay(); |  | 
| 387         if (delayToNextPost > lastGeneratedDelay) { |  | 
| 388             Log.w(TAG, "Delay to next post attempt (" + delayToNextPost |  | 
| 389                             + ") is greater than expected (" + lastGeneratedDela
     y |  | 
| 390                             + ").  Resetting to now."); |  | 
| 391             mTimestampForNextPostAttempt = currentTime; |  | 
| 392         } |  | 
| 393 |  | 
| 394         migrateToNewerChromeVersions(); |  | 
| 395         mStateHasBeenRestored = true; |  | 
| 396     } |  | 
| 397 |  | 
| 398     /** |  | 
| 399      * Writes out the current state to a file. |  | 
| 400      */ |  | 
| 401     private void saveState(Context context) { |  | 
| 402         SharedPreferences prefs = OmahaBase.getSharedPreferences(context); |  | 
| 403         SharedPreferences.Editor editor = prefs.edit(); |  | 
| 404         editor.putBoolean(OmahaBase.PREF_SEND_INSTALL_EVENT, mSendInstallEvent); |  | 
| 405         editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_INSTALL, mTimestampOfInstall)
     ; |  | 
| 406         editor.putLong( |  | 
| 407                 OmahaBase.PREF_TIMESTAMP_FOR_NEXT_POST_ATTEMPT, mTimestampForNex
     tPostAttempt); |  | 
| 408         editor.putLong(OmahaBase.PREF_TIMESTAMP_FOR_NEW_REQUEST, mTimestampForNe
     wRequest); |  | 
| 409         editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, |  | 
| 410                 hasRequest() ? mCurrentRequest.getCreationTimestamp() : INVALID_
     TIMESTAMP); |  | 
| 411         editor.putString(OmahaBase.PREF_PERSISTED_REQUEST_ID, |  | 
| 412                 hasRequest() ? mCurrentRequest.getRequestID() : INVALID_REQUEST_
     ID); |  | 
| 413         editor.putString( |  | 
| 414                 OmahaBase.PREF_LATEST_VERSION, mLatestVersion == null ? "" : mLa
     testVersion); |  | 
| 415         editor.putString(OmahaBase.PREF_MARKET_URL, mMarketURL == null ? "" : mM
     arketURL); |  | 
| 416         editor.putString(OmahaBase.PREF_INSTALL_SOURCE, mInstallSource); |  | 
| 417         editor.apply(); |  | 
| 418 |  | 
| 419         mDelegate.onSaveStateDone(mTimestampForNewRequest, mTimestampForNextPost
     Attempt); |  | 
| 420     } |  | 
| 421 |  | 
| 422     private void migrateToNewerChromeVersions() { |  | 
| 423         // Remove any repeating alarms in favor of the new scheduling setup on M
     58 and up. |  | 
| 424         // Seems cheaper to cancel the alarm repeatedly than to store a SharedPr
     eference and never |  | 
| 425         // do it again. |  | 
| 426         Intent intent = new Intent(getContext(), OmahaClient.class); |  | 
| 427         intent.setAction(ACTION_REGISTER_REQUEST); |  | 
| 428         getBackoffScheduler().cancelAlarm(intent); |  | 
| 429     } |  | 
| 430 |  | 
| 431     Context getContext() { |  | 
| 432         return mDelegate.getContext(); |  | 
| 433     } |  | 
| 434 |  | 
| 435     private RequestGenerator getRequestGenerator() { |  | 
| 436         return mDelegate.getRequestGenerator(); |  | 
| 437     } |  | 
| 438 |  | 
| 439     private ExponentialBackoffScheduler getBackoffScheduler() { |  | 
| 440         return mDelegate.getScheduler(); |  | 
| 441     } |  | 
| 442 |  | 
| 443     void setDelegateForTests(OmahaDelegate delegate) { |  | 
| 444         mDelegate = delegate; |  | 
| 445     } |  | 
| 446 } | 33 } | 
| OLD | NEW | 
|---|