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 
     28 import java.util.HashSet;
     29 
     30 /**
     31  * Helper class for monitoring the state of packages: adding, removing,
     32  * updating, and disappearing and reappearing on the SD card.
     33  */
     34 public abstract class PackageMonitor extends android.content.BroadcastReceiver {
     35     static final IntentFilter sPackageFilt = new IntentFilter();
     36     static final IntentFilter sNonDataFilt = new IntentFilter();
     37     static final IntentFilter sExternalFilt = new IntentFilter();
     38 
     39     static final Object sLock = new Object();
     40     static HandlerThread sBackgroundThread;
     41     static Handler sBackgroundHandler;
     42 
     43     static {
     44         sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
     45         sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
     46         sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
     47         sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
     48         sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
     49         sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
     50         sPackageFilt.addDataScheme("package");
     51         sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
     52         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
     53         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
     54     }
     55 
     56     final HashSet<String> mUpdatingPackages = new HashSet<String>();
     57 
     58     Context mRegisteredContext;
     59     Handler mRegisteredHandler;
     60     String[] mDisappearingPackages;
     61     String[] mAppearingPackages;
     62     String[] mModifiedPackages;
     63     int mChangeType;
     64     boolean mSomePackagesChanged;
     65 
     66     String[] mTempArray = new String[1];
     67 
     68     public void register(Context context, Looper thread, boolean externalStorage) {
     69         if (mRegisteredContext != null) {
     70             throw new IllegalStateException("Already registered");
     71         }
     72         mRegisteredContext = context;
     73         if (thread == null) {
     74             synchronized (sLock) {
     75                 if (sBackgroundThread == null) {
     76                     sBackgroundThread = new HandlerThread("PackageMonitor",
     77                             android.os.Process.THREAD_PRIORITY_BACKGROUND);
     78                     sBackgroundThread.start();
     79                     sBackgroundHandler = new Handler(sBackgroundThread.getLooper());
     80                 }
     81                 mRegisteredHandler = sBackgroundHandler;
     82             }
     83         } else {
     84             mRegisteredHandler = new Handler(thread);
     85         }
     86         context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler);
     87         context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler);
     88         if (externalStorage) {
     89             context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler);
     90         }
     91     }
     92 
     93     public Handler getRegisteredHandler() {
     94         return mRegisteredHandler;
     95     }
     96 
     97     public void unregister() {
     98         if (mRegisteredContext == null) {
     99             throw new IllegalStateException("Not registered");
    100         }
    101         mRegisteredContext.unregisterReceiver(this);
    102         mRegisteredContext = null;
    103     }
    104 
    105     //not yet implemented
    106     boolean isPackageUpdating(String packageName) {
    107         synchronized (mUpdatingPackages) {
    108             return mUpdatingPackages.contains(packageName);
    109         }
    110     }
    111 
    112     public void onBeginPackageChanges() {
    113     }
    114 
    115     public void onPackageAdded(String packageName, int uid) {
    116     }
    117 
    118     public void onPackageRemoved(String packageName, int uid) {
    119     }
    120 
    121     public void onPackageUpdateStarted(String packageName, int uid) {
    122     }
    123 
    124     public void onPackageUpdateFinished(String packageName, int uid) {
    125     }
    126 
    127     public void onPackageChanged(String packageName, int uid, String[] components) {
    128     }
    129 
    130     public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
    131         return false;
    132     }
    133 
    134     public void onUidRemoved(int uid) {
    135     }
    136 
    137     public void onPackagesAvailable(String[] packages) {
    138     }
    139 
    140     public void onPackagesUnavailable(String[] packages) {
    141     }
    142 
    143     public static final int PACKAGE_UNCHANGED = 0;
    144     public static final int PACKAGE_UPDATING = 1;
    145     public static final int PACKAGE_TEMPORARY_CHANGE = 2;
    146     public static final int PACKAGE_PERMANENT_CHANGE = 3;
    147 
    148     public void onPackageDisappeared(String packageName, int reason) {
    149     }
    150 
    151     public void onPackageAppeared(String packageName, int reason) {
    152     }
    153 
    154     public void onPackageModified(String packageName) {
    155     }
    156 
    157     public boolean didSomePackagesChange() {
    158         return mSomePackagesChanged;
    159     }
    160 
    161     public int isPackageAppearing(String packageName) {
    162         if (mAppearingPackages != null) {
    163             for (int i=mAppearingPackages.length-1; i>=0; i--) {
    164                 if (packageName.equals(mAppearingPackages[i])) {
    165                     return mChangeType;
    166                 }
    167             }
    168         }
    169         return PACKAGE_UNCHANGED;
    170     }
    171 
    172     public boolean anyPackagesAppearing() {
    173         return mAppearingPackages != null;
    174     }
    175 
    176     public int isPackageDisappearing(String packageName) {
    177         if (mDisappearingPackages != null) {
    178             for (int i=mDisappearingPackages.length-1; i>=0; i--) {
    179                 if (packageName.equals(mDisappearingPackages[i])) {
    180                     return mChangeType;
    181                 }
    182             }
    183         }
    184         return PACKAGE_UNCHANGED;
    185     }
    186 
    187     public boolean anyPackagesDisappearing() {
    188         return mDisappearingPackages != null;
    189     }
    190 
    191     public boolean isPackageModified(String packageName) {
    192         if (mModifiedPackages != null) {
    193             for (int i=mModifiedPackages.length-1; i>=0; i--) {
    194                 if (packageName.equals(mModifiedPackages[i])) {
    195                     return true;
    196                 }
    197             }
    198         }
    199         return false;
    200     }
    201 
    202     public void onSomePackagesChanged() {
    203     }
    204 
    205     public void onFinishPackageChanges() {
    206     }
    207 
    208     String getPackageName(Intent intent) {
    209         Uri uri = intent.getData();
    210         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
    211         return pkg;
    212     }
    213 
    214     @Override
    215     public void onReceive(Context context, Intent intent) {
    216         onBeginPackageChanges();
    217 
    218         mDisappearingPackages = mAppearingPackages = null;
    219         mSomePackagesChanged = false;
    220 
    221         String action = intent.getAction();
    222         if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
    223             String pkg = getPackageName(intent);
    224             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
    225             // We consider something to have changed regardless of whether
    226             // this is just an update, because the update is now finished
    227             // and the contents of the package may have changed.
    228             mSomePackagesChanged = true;
    229             if (pkg != null) {
    230                 mAppearingPackages = mTempArray;
    231                 mTempArray[0] = pkg;
    232                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    233                     mModifiedPackages = mTempArray;
    234                     mChangeType = PACKAGE_UPDATING;
    235                     onPackageUpdateFinished(pkg, uid);
    236                     onPackageModified(pkg);
    237                 } else {
    238                     mChangeType = PACKAGE_PERMANENT_CHANGE;
    239                     onPackageAdded(pkg, uid);
    240                 }
    241                 onPackageAppeared(pkg, mChangeType);
    242                 if (mChangeType == PACKAGE_UPDATING) {
    243                     synchronized (mUpdatingPackages) {
    244                         mUpdatingPackages.remove(pkg);
    245                     }
    246                 }
    247             }
    248         } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
    249             String pkg = getPackageName(intent);
    250             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
    251             if (pkg != null) {
    252                 mDisappearingPackages = mTempArray;
    253                 mTempArray[0] = pkg;
    254                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    255                     mChangeType = PACKAGE_UPDATING;
    256                     synchronized (mUpdatingPackages) {
    257                         //not used for now
    258                         //mUpdatingPackages.add(pkg);
    259                     }
    260                     onPackageUpdateStarted(pkg, uid);
    261                 } else {
    262                     mChangeType = PACKAGE_PERMANENT_CHANGE;
    263                     // We only consider something to have changed if this is
    264                     // not a replace; for a replace, we just need to consider
    265                     // it when it is re-added.
    266                     mSomePackagesChanged = true;
    267                     onPackageRemoved(pkg, uid);
    268                 }
    269                 onPackageDisappeared(pkg, mChangeType);
    270             }
    271         } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
    272             String pkg = getPackageName(intent);
    273             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
    274             String[] components = intent.getStringArrayExtra(
    275                     Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
    276             if (pkg != null) {
    277                 mModifiedPackages = mTempArray;
    278                 mTempArray[0] = pkg;
    279                 onPackageChanged(pkg, uid, components);
    280                 // XXX Don't want this to always cause mSomePackagesChanged,
    281                 // since it can happen a fair amount.
    282                 onPackageModified(pkg);
    283             }
    284         } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
    285             mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
    286             mChangeType = PACKAGE_TEMPORARY_CHANGE;
    287             boolean canRestart = onHandleForceStop(intent,
    288                     mDisappearingPackages,
    289                     intent.getIntExtra(Intent.EXTRA_UID, 0), false);
    290             if (canRestart) setResultCode(Activity.RESULT_OK);
    291         } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
    292             mDisappearingPackages = new String[] {getPackageName(intent)};
    293             mChangeType = PACKAGE_TEMPORARY_CHANGE;
    294             onHandleForceStop(intent, mDisappearingPackages,
    295                     intent.getIntExtra(Intent.EXTRA_UID, 0), true);
    296         } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
    297             onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
    298         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
    299             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    300             mAppearingPackages = pkgList;
    301             mChangeType = PACKAGE_TEMPORARY_CHANGE;
    302             mSomePackagesChanged = true;
    303             if (pkgList != null) {
    304                 onPackagesAvailable(pkgList);
    305                 for (int i=0; i<pkgList.length; i++) {
    306                     onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
    307                 }
    308             }
    309         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
    310             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
    311             mDisappearingPackages = pkgList;
    312             mChangeType = PACKAGE_TEMPORARY_CHANGE;
    313             mSomePackagesChanged = true;
    314             if (pkgList != null) {
    315                 onPackagesUnavailable(pkgList);
    316                 for (int i=0; i<pkgList.length; i++) {
    317                     onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
    318                 }
    319             }
    320         }
    321 
    322         if (mSomePackagesChanged) {
    323             onSomePackagesChanged();
    324         }
    325 
    326         onFinishPackageChanges();
    327     }
    328 }
    329