1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.chrome.browser.banners;
6
7import android.content.pm.PackageInfo;
8import android.content.pm.PackageManager;
9import android.os.Handler;
10import android.os.Looper;
11import android.os.SystemClock;
12import android.text.TextUtils;
13
14import java.util.List;
15
16/**
17 * Monitors the PackageManager to see when an app has been installed.
18 */
19public class InstallerDelegate implements Runnable {
20    /**
21     * Callback for when the app install has completed.
22     */
23    public static interface Observer {
24        /**
25         * Called when the task has finished.
26         * @param delegate Instance of the class that finished.
27         * @param success  Whether or not the package was successfully installed.
28         */
29        public void onInstallFinished(InstallerDelegate delegate, boolean success);
30    }
31
32    private static final long DEFAULT_MS_BETWEEN_RUNS = 1000;
33    private static final long DEFAULT_MS_MAXIMUM_WAITING_TIME = 3 * 60 * 1000;
34
35    /** Message loop to post the Runnable to. */
36    private final Handler mHandler;
37
38    /** PackageManager that the Runnable is monitoring. */
39    private final PackageManager mPackageManager;
40
41    /** Object that is notified when the PackageManager has finished. */
42    private final Observer mObserver;
43
44    /** Name of the package that we need to see the PackageManager has finished installing. */
45    private final String mPackageName;
46
47    /** Whether or not the Runnable is currently looping. */
48    private boolean mIsRunning;
49
50    /** Number of milliseconds to wait between calls to run(). */
51    private long mMsBetweenRuns;
52
53    /** Maximum number of milliseconds to wait before giving up. */
54    private long mMsMaximumWaitingTime;
55
56    /** Timestamp of when we first started. */
57    private long mTimestampStarted;
58
59    /**
60     * Constructs the InstallerDelegate.
61     * @param looper         Thread to run the Runnable on.
62     * @param packageManager Provides access to the list of installed apps.
63     * @param observer       Alerted when the package has been completely installed.
64     * @param packageName    Name of the package for the app to monitor.
65     */
66    InstallerDelegate(
67            Looper looper, PackageManager packageManager, Observer observer, String packageName) {
68        mHandler = new Handler(looper);
69        mPackageManager = packageManager;
70        mObserver = observer;
71        mPackageName = packageName;
72        mMsBetweenRuns = DEFAULT_MS_BETWEEN_RUNS;
73        mMsMaximumWaitingTime = DEFAULT_MS_MAXIMUM_WAITING_TIME;
74    }
75
76    /**
77     * Begin monitoring the PackageManager to see if it completes installing the package.
78     */
79    public void start() {
80        mTimestampStarted = SystemClock.elapsedRealtime();
81        mIsRunning = true;
82        mHandler.postDelayed(this, mMsBetweenRuns);
83    }
84
85    /**
86     * Don't call this directly; instead, call {@link #start()}.
87     */
88    @Override
89    public void run() {
90        boolean isInstalled = isInstalled();
91        boolean waitedTooLong =
92                (SystemClock.elapsedRealtime() - mTimestampStarted) > mMsMaximumWaitingTime;
93        if (isInstalled || !mIsRunning || waitedTooLong) {
94            mObserver.onInstallFinished(this, isInstalled);
95            mIsRunning = false;
96        } else {
97            mHandler.postDelayed(this, mMsBetweenRuns);
98        }
99    }
100
101    /**
102     * Checks if the app has been installed on the system.
103     * @return True if the PackageManager reports that the app is installed, false otherwise.
104     */
105    private boolean isInstalled() {
106        List<PackageInfo> packs = mPackageManager.getInstalledPackages(0);
107        for (int i = 0; i < packs.size(); i++) {
108            if (TextUtils.equals(packs.get(i).packageName, mPackageName)) return true;
109        }
110        return false;
111    }
112
113    /**
114     * Prevent rescheduling the Runnable.
115     */
116    void cancel() {
117        mIsRunning = false;
118    }
119
120    /**
121     * Checks to see if the Runnable will continue scheduling itself.
122     * @return True if the runnable is still being scheduled.
123     */
124    boolean isRunning() {
125        return mIsRunning;
126    }
127
128    /**
129     * Set how often the handler will check the PackageManager.
130     * @param msBetween How long to wait between executions of the Runnable.
131     * @param msMax     How long to wait before giving up.
132     */
133    void setTimingForTests(long msBetween, long msMax) {
134        mMsBetweenRuns = msBetween;
135        mMsMaximumWaitingTime = msMax;
136    }
137}
138