PackageMonitor.java revision 80a4af2bbc6af42ae605e454bf89558e564f5244
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.content;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.HandlerThread;
26import android.os.Looper;
27
28import java.util.HashSet;
29
30/**
31 * Helper class for monitoring the state of packages: adding, removing,
32 * updating, and disappearing and reappearing on the SD card.
33 */
34public abstract class PackageMonitor extends android.content.BroadcastReceiver {
35    static final IntentFilter sPackageFilt = new IntentFilter();
36    static final IntentFilter sNonDataFilt = new IntentFilter();
37    static final IntentFilter sExternalFilt = new IntentFilter();
38
39    static final Object sLock = new Object();
40    static HandlerThread sBackgroundThread;
41    static Handler sBackgroundHandler;
42
43    static {
44        sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
45        sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
46        sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
47        sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
48        sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
49        sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
50        sPackageFilt.addDataScheme("package");
51        sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
52        sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
53        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
54        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
55    }
56
57    final HashSet<String> mUpdatingPackages = new HashSet<String>();
58
59    Context mRegisteredContext;
60    Handler mRegisteredHandler;
61    String[] mDisappearingPackages;
62    String[] mAppearingPackages;
63    String[] mModifiedPackages;
64    int mChangeType;
65    boolean mSomePackagesChanged;
66
67    String[] mTempArray = new String[1];
68
69    public void register(Context context, Looper thread, boolean externalStorage) {
70        if (mRegisteredContext != null) {
71            throw new IllegalStateException("Already registered");
72        }
73        mRegisteredContext = context;
74        if (thread == null) {
75            synchronized (sLock) {
76                if (sBackgroundThread == null) {
77                    sBackgroundThread = new HandlerThread("PackageMonitor",
78                            android.os.Process.THREAD_PRIORITY_BACKGROUND);
79                    sBackgroundThread.start();
80                    sBackgroundHandler = new Handler(sBackgroundThread.getLooper());
81                }
82                mRegisteredHandler = sBackgroundHandler;
83            }
84        } else {
85            mRegisteredHandler = new Handler(thread);
86        }
87        context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler);
88        context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler);
89        if (externalStorage) {
90            context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler);
91        }
92    }
93
94    public Handler getRegisteredHandler() {
95        return mRegisteredHandler;
96    }
97
98    public void unregister() {
99        if (mRegisteredContext == null) {
100            throw new IllegalStateException("Not registered");
101        }
102        mRegisteredContext.unregisterReceiver(this);
103        mRegisteredContext = null;
104    }
105
106    //not yet implemented
107    boolean isPackageUpdating(String packageName) {
108        synchronized (mUpdatingPackages) {
109            return mUpdatingPackages.contains(packageName);
110        }
111    }
112
113    public void onBeginPackageChanges() {
114    }
115
116    /**
117     * Called when a package is really added (and not replaced).
118     */
119    public void onPackageAdded(String packageName, int uid) {
120    }
121
122    /**
123     * Called when a package is really removed (and not replaced).
124     */
125    public void onPackageRemoved(String packageName, int uid) {
126    }
127
128    public void onPackageUpdateStarted(String packageName, int uid) {
129    }
130
131    public void onPackageUpdateFinished(String packageName, int uid) {
132    }
133
134    public void onPackageChanged(String packageName, int uid, String[] components) {
135    }
136
137    public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
138        return false;
139    }
140
141    public void onHandleUserStop(Intent intent, int userHandle) {
142    }
143
144    public void onUidRemoved(int uid) {
145    }
146
147    public void onPackagesAvailable(String[] packages) {
148    }
149
150    public void onPackagesUnavailable(String[] packages) {
151    }
152
153    public static final int PACKAGE_UNCHANGED = 0;
154    public static final int PACKAGE_UPDATING = 1;
155    public static final int PACKAGE_TEMPORARY_CHANGE = 2;
156    public static final int PACKAGE_PERMANENT_CHANGE = 3;
157
158    /**
159     * Called when a package disappears for any reason.
160     */
161    public void onPackageDisappeared(String packageName, int reason) {
162    }
163
164    /**
165     * Called when a package appears for any reason.
166     */
167    public void onPackageAppeared(String packageName, int reason) {
168    }
169
170    public void onPackageModified(String packageName) {
171    }
172
173    public boolean didSomePackagesChange() {
174        return mSomePackagesChanged;
175    }
176
177    public int isPackageAppearing(String packageName) {
178        if (mAppearingPackages != null) {
179            for (int i=mAppearingPackages.length-1; i>=0; i--) {
180                if (packageName.equals(mAppearingPackages[i])) {
181                    return mChangeType;
182                }
183            }
184        }
185        return PACKAGE_UNCHANGED;
186    }
187
188    public boolean anyPackagesAppearing() {
189        return mAppearingPackages != null;
190    }
191
192    public int isPackageDisappearing(String packageName) {
193        if (mDisappearingPackages != null) {
194            for (int i=mDisappearingPackages.length-1; i>=0; i--) {
195                if (packageName.equals(mDisappearingPackages[i])) {
196                    return mChangeType;
197                }
198            }
199        }
200        return PACKAGE_UNCHANGED;
201    }
202
203    public boolean anyPackagesDisappearing() {
204        return mDisappearingPackages != null;
205    }
206
207    public boolean isPackageModified(String packageName) {
208        if (mModifiedPackages != null) {
209            for (int i=mModifiedPackages.length-1; i>=0; i--) {
210                if (packageName.equals(mModifiedPackages[i])) {
211                    return true;
212                }
213            }
214        }
215        return false;
216    }
217
218    public void onSomePackagesChanged() {
219    }
220
221    public void onFinishPackageChanges() {
222    }
223
224    String getPackageName(Intent intent) {
225        Uri uri = intent.getData();
226        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
227        return pkg;
228    }
229
230    @Override
231    public void onReceive(Context context, Intent intent) {
232        onBeginPackageChanges();
233
234        mDisappearingPackages = mAppearingPackages = null;
235        mSomePackagesChanged = false;
236
237        String action = intent.getAction();
238        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
239            String pkg = getPackageName(intent);
240            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
241            // We consider something to have changed regardless of whether
242            // this is just an update, because the update is now finished
243            // and the contents of the package may have changed.
244            mSomePackagesChanged = true;
245            if (pkg != null) {
246                mAppearingPackages = mTempArray;
247                mTempArray[0] = pkg;
248                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
249                    mModifiedPackages = mTempArray;
250                    mChangeType = PACKAGE_UPDATING;
251                    onPackageUpdateFinished(pkg, uid);
252                    onPackageModified(pkg);
253                } else {
254                    mChangeType = PACKAGE_PERMANENT_CHANGE;
255                    onPackageAdded(pkg, uid);
256                }
257                onPackageAppeared(pkg, mChangeType);
258                if (mChangeType == PACKAGE_UPDATING) {
259                    synchronized (mUpdatingPackages) {
260                        mUpdatingPackages.remove(pkg);
261                    }
262                }
263            }
264        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
265            String pkg = getPackageName(intent);
266            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
267            if (pkg != null) {
268                mDisappearingPackages = mTempArray;
269                mTempArray[0] = pkg;
270                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
271                    mChangeType = PACKAGE_UPDATING;
272                    synchronized (mUpdatingPackages) {
273                        //not used for now
274                        //mUpdatingPackages.add(pkg);
275                    }
276                    onPackageUpdateStarted(pkg, uid);
277                } else {
278                    mChangeType = PACKAGE_PERMANENT_CHANGE;
279                    // We only consider something to have changed if this is
280                    // not a replace; for a replace, we just need to consider
281                    // it when it is re-added.
282                    mSomePackagesChanged = true;
283                    onPackageRemoved(pkg, uid);
284                }
285                onPackageDisappeared(pkg, mChangeType);
286            }
287        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
288            String pkg = getPackageName(intent);
289            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
290            String[] components = intent.getStringArrayExtra(
291                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
292            if (pkg != null) {
293                mModifiedPackages = mTempArray;
294                mTempArray[0] = pkg;
295                onPackageChanged(pkg, uid, components);
296                // XXX Don't want this to always cause mSomePackagesChanged,
297                // since it can happen a fair amount.
298                onPackageModified(pkg);
299            }
300        } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
301            mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
302            mChangeType = PACKAGE_TEMPORARY_CHANGE;
303            boolean canRestart = onHandleForceStop(intent,
304                    mDisappearingPackages,
305                    intent.getIntExtra(Intent.EXTRA_UID, 0), false);
306            if (canRestart) setResultCode(Activity.RESULT_OK);
307        } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
308            mDisappearingPackages = new String[] {getPackageName(intent)};
309            mChangeType = PACKAGE_TEMPORARY_CHANGE;
310            onHandleForceStop(intent, mDisappearingPackages,
311                    intent.getIntExtra(Intent.EXTRA_UID, 0), true);
312        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
313            onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
314        } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
315            if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
316                onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
317            }
318        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
319            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
320            mAppearingPackages = pkgList;
321            mChangeType = PACKAGE_TEMPORARY_CHANGE;
322            mSomePackagesChanged = true;
323            if (pkgList != null) {
324                onPackagesAvailable(pkgList);
325                for (int i=0; i<pkgList.length; i++) {
326                    onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
327                }
328            }
329        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
330            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
331            mDisappearingPackages = pkgList;
332            mChangeType = PACKAGE_TEMPORARY_CHANGE;
333            mSomePackagesChanged = true;
334            if (pkgList != null) {
335                onPackagesUnavailable(pkgList);
336                for (int i=0; i<pkgList.length; i++) {
337                    onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
338                }
339            }
340        }
341
342        if (mSomePackagesChanged) {
343            onSomePackagesChanged();
344        }
345
346        onFinishPackageChanges();
347    }
348}
349