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.HandlerThread;
     26 import android.os.Looper;
     27 import android.os.UserHandle;
     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 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 = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
    376                     ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
    377             mSomePackagesChanged = true;
    378             if (pkgList != null) {
    379                 onPackagesAvailable(pkgList);
    380                 for (int i=0; i<pkgList.length; i++) {
    381                     onPackageAppeared(pkgList[i], mChangeType);
    382                 }
    383             }
    384         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
    385             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    386             mDisappearingPackages = pkgList;
    387             mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
    388                     ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
    389             mSomePackagesChanged = true;
    390             if (pkgList != null) {
    391                 onPackagesUnavailable(pkgList);
    392                 for (int i=0; i<pkgList.length; i++) {
    393                     onPackageDisappeared(pkgList[i], mChangeType);
    394                 }
    395             }
    396         }
    397 
    398         if (mSomePackagesChanged) {
    399             onSomePackagesChanged();
    400         }
    401 
    402         onFinishPackageChanges();
    403         mChangeUserId = UserHandle.USER_NULL;
    404     }
    405 }
    406