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