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.Looper;
26import android.os.UserHandle;
27import android.util.Slog;
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 isReplacing() {
248        return mChangeType == PACKAGE_UPDATING;
249    }
250
251    public boolean isPackageModified(String packageName) {
252        if (mModifiedPackages != null) {
253            for (int i=mModifiedPackages.length-1; i>=0; i--) {
254                if (packageName.equals(mModifiedPackages[i])) {
255                    return true;
256                }
257            }
258        }
259        return false;
260    }
261
262    public void onSomePackagesChanged() {
263    }
264
265    public void onFinishPackageChanges() {
266    }
267
268    public int getChangingUserId() {
269        return mChangeUserId;
270    }
271
272    String getPackageName(Intent intent) {
273        Uri uri = intent.getData();
274        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
275        return pkg;
276    }
277
278    @Override
279    public void onReceive(Context context, Intent intent) {
280        mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
281                UserHandle.USER_NULL);
282        if (mChangeUserId == UserHandle.USER_NULL) {
283            Slog.w("PackageMonitor", "Intent broadcast does not contain user handle: " + intent);
284            return;
285        }
286        onBeginPackageChanges();
287
288        mDisappearingPackages = mAppearingPackages = null;
289        mSomePackagesChanged = false;
290
291        String action = intent.getAction();
292        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
293            String pkg = getPackageName(intent);
294            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
295            // We consider something to have changed regardless of whether
296            // this is just an update, because the update is now finished
297            // and the contents of the package may have changed.
298            mSomePackagesChanged = true;
299            if (pkg != null) {
300                mAppearingPackages = mTempArray;
301                mTempArray[0] = pkg;
302                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
303                    mModifiedPackages = mTempArray;
304                    mChangeType = PACKAGE_UPDATING;
305                    onPackageUpdateFinished(pkg, uid);
306                    onPackageModified(pkg);
307                } else {
308                    mChangeType = PACKAGE_PERMANENT_CHANGE;
309                    onPackageAdded(pkg, uid);
310                }
311                onPackageAppeared(pkg, mChangeType);
312                if (mChangeType == PACKAGE_UPDATING) {
313                    synchronized (mUpdatingPackages) {
314                        mUpdatingPackages.remove(pkg);
315                    }
316                }
317            }
318        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
319            String pkg = getPackageName(intent);
320            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
321            if (pkg != null) {
322                mDisappearingPackages = mTempArray;
323                mTempArray[0] = pkg;
324                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
325                    mChangeType = PACKAGE_UPDATING;
326                    synchronized (mUpdatingPackages) {
327                        //not used for now
328                        //mUpdatingPackages.add(pkg);
329                    }
330                    onPackageUpdateStarted(pkg, uid);
331                } else {
332                    mChangeType = PACKAGE_PERMANENT_CHANGE;
333                    // We only consider something to have changed if this is
334                    // not a replace; for a replace, we just need to consider
335                    // it when it is re-added.
336                    mSomePackagesChanged = true;
337                    onPackageRemoved(pkg, uid);
338                    if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) {
339                        onPackageRemovedAllUsers(pkg, uid);
340                    }
341                }
342                onPackageDisappeared(pkg, mChangeType);
343            }
344        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
345            String pkg = getPackageName(intent);
346            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
347            String[] components = intent.getStringArrayExtra(
348                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
349            if (pkg != null) {
350                mModifiedPackages = mTempArray;
351                mTempArray[0] = pkg;
352                mChangeType = PACKAGE_PERMANENT_CHANGE;
353                if (onPackageChanged(pkg, uid, components)) {
354                    mSomePackagesChanged = true;
355                }
356                onPackageModified(pkg);
357            }
358        } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
359            mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
360            mChangeType = PACKAGE_TEMPORARY_CHANGE;
361            boolean canRestart = onHandleForceStop(intent,
362                    mDisappearingPackages,
363                    intent.getIntExtra(Intent.EXTRA_UID, 0), false);
364            if (canRestart) setResultCode(Activity.RESULT_OK);
365        } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
366            mDisappearingPackages = new String[] {getPackageName(intent)};
367            mChangeType = PACKAGE_TEMPORARY_CHANGE;
368            onHandleForceStop(intent, mDisappearingPackages,
369                    intent.getIntExtra(Intent.EXTRA_UID, 0), true);
370        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
371            onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
372        } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
373            if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
374                onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
375            }
376        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
377            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
378            mAppearingPackages = pkgList;
379            mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
380                    ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
381            mSomePackagesChanged = true;
382            if (pkgList != null) {
383                onPackagesAvailable(pkgList);
384                for (int i=0; i<pkgList.length; i++) {
385                    onPackageAppeared(pkgList[i], mChangeType);
386                }
387            }
388        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
389            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
390            mDisappearingPackages = pkgList;
391            mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
392                    ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
393            mSomePackagesChanged = true;
394            if (pkgList != null) {
395                onPackagesUnavailable(pkgList);
396                for (int i=0; i<pkgList.length; i++) {
397                    onPackageDisappeared(pkgList[i], mChangeType);
398                }
399            }
400        }
401
402        if (mSomePackagesChanged) {
403            onSomePackagesChanged();
404        }
405
406        onFinishPackageChanges();
407        mChangeUserId = UserHandle.USER_NULL;
408    }
409}
410