Home | History | Annotate | Download | only in content
      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 
     17 package com.android.internal.content;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.net.Uri;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.os.UserHandle;
     27 import android.util.Slog;
     28 import com.android.internal.os.BackgroundThread;
     29 
     30 import 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  */
     36 public 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