PackageMonitor.java revision 8d044e8bc287c1a567d82aedbe30085b011544c3
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;
27import android.os.UserHandle;
28import com.android.internal.os.BackgroundThread;
29
30import java.util.HashSet;
31
32/**
33 * Helper class for monitoring the state of packages: adding, removing,
34 * updating, and disappearing and reappearing on the SD card.
35 */
36public abstract class PackageMonitor extends android.content.BroadcastReceiver {
37    static final IntentFilter sPackageFilt = new IntentFilter();
38    static final IntentFilter sNonDataFilt = new IntentFilter();
39    static final IntentFilter sExternalFilt = new IntentFilter();
40
41    static {
42        sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
43        sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
44        sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
45        sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
46        sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
47        sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
48        sPackageFilt.addDataScheme("package");
49        sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
50        sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
51        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
52        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
53    }
54
55    final HashSet<String> mUpdatingPackages = new HashSet<String>();
56
57    Context mRegisteredContext;
58    Handler mRegisteredHandler;
59    String[] mDisappearingPackages;
60    String[] mAppearingPackages;
61    String[] mModifiedPackages;
62    int mChangeType;
63    int mChangeUserId = UserHandle.USER_NULL;
64    boolean mSomePackagesChanged;
65
66    String[] mTempArray = new String[1];
67
68    public void register(Context context, Looper thread, boolean externalStorage) {
69        register(context, thread, null, externalStorage);
70    }
71
72    public void register(Context context, Looper thread, UserHandle user,
73            boolean externalStorage) {
74        if (mRegisteredContext != null) {
75            throw new IllegalStateException("Already registered");
76        }
77        mRegisteredContext = context;
78        if (thread == null) {
79            mRegisteredHandler = BackgroundThread.getHandler();
80        } else {
81            mRegisteredHandler = new Handler(thread);
82        }
83        if (user != null) {
84            context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler);
85            context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler);
86            if (externalStorage) {
87                context.registerReceiverAsUser(this, user, sExternalFilt, null,
88                        mRegisteredHandler);
89            }
90        } else {
91            context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler);
92            context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler);
93            if (externalStorage) {
94                context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler);
95            }
96        }
97    }
98
99    public Handler getRegisteredHandler() {
100        return mRegisteredHandler;
101    }
102
103    public void unregister() {
104        if (mRegisteredContext == null) {
105            throw new IllegalStateException("Not registered");
106        }
107        mRegisteredContext.unregisterReceiver(this);
108        mRegisteredContext = null;
109    }
110
111    //not yet implemented
112    boolean isPackageUpdating(String packageName) {
113        synchronized (mUpdatingPackages) {
114            return mUpdatingPackages.contains(packageName);
115        }
116    }
117
118    public void onBeginPackageChanges() {
119    }
120
121    /**
122     * Called when a package is really added (and not replaced).
123     */
124    public void onPackageAdded(String packageName, int uid) {
125    }
126
127    /**
128     * Called when a package is really removed (and not replaced).
129     */
130    public void onPackageRemoved(String packageName, int uid) {
131    }
132
133    /**
134     * Called when a package is really removed (and not replaced) for
135     * all users on the device.
136     */
137    public void onPackageRemovedAllUsers(String packageName, int uid) {
138    }
139
140    public void onPackageUpdateStarted(String packageName, int uid) {
141    }
142
143    public void onPackageUpdateFinished(String packageName, int uid) {
144    }
145
146    /**
147     * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
148     * Intent.ACTION_PACKAGE_CHANGED} being received, informing you of
149     * changes to the enabled/disabled state of components in a package
150     * and/or of the overall package.
151     *
152     * @param packageName The name of the package that is changing.
153     * @param uid The user ID the package runs under.
154     * @param components Any components in the package that are changing.  If
155     * the overall package is changing, this will contain an entry of the
156     * package name itself.
157     * @return Return true to indicate you care about this change, which will
158     * result in {@link #onSomePackagesChanged()} being called later.  If you
159     * return false, no further callbacks will happen about this change.  The
160     * default implementation returns true if this is a change to the entire
161     * package.
162     */
163    public boolean onPackageChanged(String packageName, int uid, String[] components) {
164        if (components != null) {
165            for (String name : components) {
166                if (packageName.equals(name)) {
167                    return true;
168                }
169            }
170        }
171        return false;
172    }
173
174    public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
175        return false;
176    }
177
178    public void onHandleUserStop(Intent intent, int userHandle) {
179    }
180
181    public void onUidRemoved(int uid) {
182    }
183
184    public void onPackagesAvailable(String[] packages) {
185    }
186
187    public void onPackagesUnavailable(String[] packages) {
188    }
189
190    public static final int PACKAGE_UNCHANGED = 0;
191    public static final int PACKAGE_UPDATING = 1;
192    public static final int PACKAGE_TEMPORARY_CHANGE = 2;
193    public static final int PACKAGE_PERMANENT_CHANGE = 3;
194
195    /**
196     * Called when a package disappears for any reason.
197     */
198    public void onPackageDisappeared(String packageName, int reason) {
199    }
200
201    /**
202     * Called when a package appears for any reason.
203     */
204    public void onPackageAppeared(String packageName, int reason) {
205    }
206
207    /**
208     * Called when an existing package is updated or its disabled state changes.
209     */
210    public void onPackageModified(String packageName) {
211    }
212
213    public boolean didSomePackagesChange() {
214        return mSomePackagesChanged;
215    }
216
217    public int isPackageAppearing(String packageName) {
218        if (mAppearingPackages != null) {
219            for (int i=mAppearingPackages.length-1; i>=0; i--) {
220                if (packageName.equals(mAppearingPackages[i])) {
221                    return mChangeType;
222                }
223            }
224        }
225        return PACKAGE_UNCHANGED;
226    }
227
228    public boolean anyPackagesAppearing() {
229        return mAppearingPackages != null;
230    }
231
232    public int isPackageDisappearing(String packageName) {
233        if (mDisappearingPackages != null) {
234            for (int i=mDisappearingPackages.length-1; i>=0; i--) {
235                if (packageName.equals(mDisappearingPackages[i])) {
236                    return mChangeType;
237                }
238            }
239        }
240        return PACKAGE_UNCHANGED;
241    }
242
243    public boolean anyPackagesDisappearing() {
244        return mDisappearingPackages != null;
245    }
246
247    public boolean isPackageModified(String packageName) {
248        if (mModifiedPackages != null) {
249            for (int i=mModifiedPackages.length-1; i>=0; i--) {
250                if (packageName.equals(mModifiedPackages[i])) {
251                    return true;
252                }
253            }
254        }
255        return false;
256    }
257
258    public void onSomePackagesChanged() {
259    }
260
261    public void onFinishPackageChanges() {
262    }
263
264    public int getChangingUserId() {
265        return mChangeUserId;
266    }
267
268    String getPackageName(Intent intent) {
269        Uri uri = intent.getData();
270        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
271        return pkg;
272    }
273
274    @Override
275    public void onReceive(Context context, Intent intent) {
276        mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
277                UserHandle.USER_NULL);
278        if (mChangeUserId == UserHandle.USER_NULL) {
279            throw new IllegalArgumentException(
280                    "Intent broadcast does not contain user handle: " + intent);
281        }
282        onBeginPackageChanges();
283
284        mDisappearingPackages = mAppearingPackages = null;
285        mSomePackagesChanged = false;
286
287        String action = intent.getAction();
288        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
289            String pkg = getPackageName(intent);
290            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
291            // We consider something to have changed regardless of whether
292            // this is just an update, because the update is now finished
293            // and the contents of the package may have changed.
294            mSomePackagesChanged = true;
295            if (pkg != null) {
296                mAppearingPackages = mTempArray;
297                mTempArray[0] = pkg;
298                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
299                    mModifiedPackages = mTempArray;
300                    mChangeType = PACKAGE_UPDATING;
301                    onPackageUpdateFinished(pkg, uid);
302                    onPackageModified(pkg);
303                } else {
304                    mChangeType = PACKAGE_PERMANENT_CHANGE;
305                    onPackageAdded(pkg, uid);
306                }
307                onPackageAppeared(pkg, mChangeType);
308                if (mChangeType == PACKAGE_UPDATING) {
309                    synchronized (mUpdatingPackages) {
310                        mUpdatingPackages.remove(pkg);
311                    }
312                }
313            }
314        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
315            String pkg = getPackageName(intent);
316            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
317            if (pkg != null) {
318                mDisappearingPackages = mTempArray;
319                mTempArray[0] = pkg;
320                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
321                    mChangeType = PACKAGE_UPDATING;
322                    synchronized (mUpdatingPackages) {
323                        //not used for now
324                        //mUpdatingPackages.add(pkg);
325                    }
326                    onPackageUpdateStarted(pkg, uid);
327                } else {
328                    mChangeType = PACKAGE_PERMANENT_CHANGE;
329                    // We only consider something to have changed if this is
330                    // not a replace; for a replace, we just need to consider
331                    // it when it is re-added.
332                    mSomePackagesChanged = true;
333                    onPackageRemoved(pkg, uid);
334                    if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) {
335                        onPackageRemovedAllUsers(pkg, uid);
336                    }
337                }
338                onPackageDisappeared(pkg, mChangeType);
339            }
340        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
341            String pkg = getPackageName(intent);
342            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
343            String[] components = intent.getStringArrayExtra(
344                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
345            if (pkg != null) {
346                mModifiedPackages = mTempArray;
347                mTempArray[0] = pkg;
348                mChangeType = PACKAGE_PERMANENT_CHANGE;
349                if (onPackageChanged(pkg, uid, components)) {
350                    mSomePackagesChanged = true;
351                }
352                onPackageModified(pkg);
353            }
354        } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
355            mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
356            mChangeType = PACKAGE_TEMPORARY_CHANGE;
357            boolean canRestart = onHandleForceStop(intent,
358                    mDisappearingPackages,
359                    intent.getIntExtra(Intent.EXTRA_UID, 0), false);
360            if (canRestart) setResultCode(Activity.RESULT_OK);
361        } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
362            mDisappearingPackages = new String[] {getPackageName(intent)};
363            mChangeType = PACKAGE_TEMPORARY_CHANGE;
364            onHandleForceStop(intent, mDisappearingPackages,
365                    intent.getIntExtra(Intent.EXTRA_UID, 0), true);
366        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
367            onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
368        } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
369            if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
370                onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
371            }
372        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
373            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
374            mAppearingPackages = pkgList;
375            mChangeType = PACKAGE_TEMPORARY_CHANGE;
376            mSomePackagesChanged = true;
377            if (pkgList != null) {
378                onPackagesAvailable(pkgList);
379                for (int i=0; i<pkgList.length; i++) {
380                    onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
381                }
382            }
383        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
384            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
385            mDisappearingPackages = pkgList;
386            mChangeType = PACKAGE_TEMPORARY_CHANGE;
387            mSomePackagesChanged = true;
388            if (pkgList != null) {
389                onPackagesUnavailable(pkgList);
390                for (int i=0; i<pkgList.length; i++) {
391                    onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
392                }
393            }
394        }
395
396        if (mSomePackagesChanged) {
397            onSomePackagesChanged();
398        }
399
400        onFinishPackageChanges();
401        mChangeUserId = UserHandle.USER_NULL;
402    }
403}
404