// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.banners; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; import org.chromium.base.VisibleForTesting; import java.util.List; /** * Monitors the PackageManager to see when an app has been installed. */ public class InstallerDelegate implements Runnable { /** * Callback for when the app install has completed. */ public static interface Observer { /** * Called when the task has finished. * @param delegate Instance of the class that finished. * @param success Whether or not the package was successfully installed. */ public void onInstallFinished(InstallerDelegate delegate, boolean success); } private static final long DEFAULT_MS_BETWEEN_RUNS = 1000; private static final long DEFAULT_MS_MAXIMUM_WAITING_TIME = 3 * 60 * 1000; /** Message loop to post the Runnable to. */ private final Handler mHandler; /** PackageManager that the Runnable is monitoring. */ private final PackageManager mPackageManager; /** Object that is notified when the PackageManager has finished. */ private final Observer mObserver; /** Name of the package that we need to see the PackageManager has finished installing. */ private final String mPackageName; /** Whether or not the Runnable is currently looping. */ private boolean mIsRunning; /** Number of milliseconds to wait between calls to run(). */ private long mMsBetweenRuns; /** Maximum number of milliseconds to wait before giving up. */ private long mMsMaximumWaitingTime; /** Timestamp of when we first started. */ private long mTimestampStarted; /** * Constructs the InstallerDelegate. * @param looper Thread to run the Runnable on. * @param packageManager Provides access to the list of installed apps. * @param observer Alerted when the package has been completely installed. * @param packageName Name of the package for the app to monitor. */ public InstallerDelegate( Looper looper, PackageManager packageManager, Observer observer, String packageName) { mHandler = new Handler(looper); mPackageManager = packageManager; mObserver = observer; mPackageName = packageName; mMsBetweenRuns = DEFAULT_MS_BETWEEN_RUNS; mMsMaximumWaitingTime = DEFAULT_MS_MAXIMUM_WAITING_TIME; } /** * Begin monitoring the PackageManager to see if it completes installing the package. */ public void start() { mTimestampStarted = SystemClock.elapsedRealtime(); mIsRunning = true; mHandler.postDelayed(this, mMsBetweenRuns); } /** * Don't call this directly; instead, call {@link #start()}. */ @Override public void run() { boolean isInstalled = isInstalled(); boolean waitedTooLong = (SystemClock.elapsedRealtime() - mTimestampStarted) > mMsMaximumWaitingTime; if (isInstalled || !mIsRunning || waitedTooLong) { mObserver.onInstallFinished(this, isInstalled); mIsRunning = false; } else { mHandler.postDelayed(this, mMsBetweenRuns); } } /** * Checks if the app has been installed on the system. * @param packageManager PackageManager to use. * @param packageName Name of the package to check. * @return True if the PackageManager reports that the app is installed, false otherwise. */ public static boolean isInstalled(PackageManager packageManager, String packageName) { List<PackageInfo> packs = packageManager.getInstalledPackages(0); for (int i = 0; i < packs.size(); i++) { if (TextUtils.equals(packs.get(i).packageName, packageName)) return true; } return false; } /** * Checks if the app has been installed on the system. * @return True if the PackageManager reports that the app is installed, false otherwise. */ private boolean isInstalled() { return isInstalled(mPackageManager, mPackageName); } /** * Prevent rescheduling the Runnable. */ public void cancel() { mIsRunning = false; } /** * Checks to see if the Runnable will continue scheduling itself. * @return True if the runnable is still being scheduled. */ @VisibleForTesting boolean isRunning() { return mIsRunning; } /** * Set how often the handler will check the PackageManager. * @param msBetween How long to wait between executions of the Runnable. * @param msMax How long to wait before giving up. */ @VisibleForTesting void setTimingForTests(long msBetween, long msMax) { mMsBetweenRuns = msBetween; mMsMaximumWaitingTime = msMax; } }