diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-02-19 17:02:21 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-02-22 11:27:52 -0800 |
commit | 21f1bd17b2dfe361acbb28453b3f3b1a110932fa (patch) | |
tree | 531c362903a1c327db99630996948da85cdedaf7 /core/java/com | |
parent | 9a56aaf12b462a064e81e02386eca8a1e77fe737 (diff) | |
download | frameworks_base-21f1bd17b2dfe361acbb28453b3f3b1a110932fa.zip frameworks_base-21f1bd17b2dfe361acbb28453b3f3b1a110932fa.tar.gz frameworks_base-21f1bd17b2dfe361acbb28453b3f3b1a110932fa.tar.bz2 |
Fix issue #2438980: Implement package watcher for voice recognizer service setting
I am getting tired of writing package monitor code, realized this is missing in
a number of places, and at this point it has gotten complicated enough that I
don't think anyone actually does it 100% right so:
Introducing PackageMonitor.
Yes there are no Java docs. I am still playing around with just what this
thing is to figure out what makes sense and how people will use it. It is
being used to fix this bug for monitoring voice recognizers (integrating the
code from the settings provider for setting an initial value), to replace
the existing code for monitoring input methods (and fix the bug where we
wouldn't remove an input method from the enabled list when it got
uninstalled), to now monitor live wallpaper package changes (now allowing
us to avoid reverting back to the default live wallpaper when the current
one is updated!), and to monitor device admin changes.
Also includes a fix so you can't uninstall an .apk that is currently enabled
as a device admin.
Also includes a fix where the default time zone was not initialized early
enough which should fix issue #2455507 (Observed Google services frame work crash).
In addition, this finally introduces a mechanism to determine if the
"force stop" button should be enabled, with convenience in PackageMonitor
for system services to handle it. All services have been updated to support
this. There is also new infrastructure for reporting battery usage as an
applicatin error report.
Diffstat (limited to 'core/java/com')
-rw-r--r-- | core/java/com/android/internal/content/PackageMonitor.java | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java new file mode 100644 index 0000000..343041f --- /dev/null +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -0,0 +1,287 @@ +package com.android.internal.content; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; + +import java.util.HashSet; + +/** + * Helper class for monitoring the state of packages: adding, removing, + * updating, and disappearing and reappearing on the SD card. + */ +public abstract class PackageMonitor extends android.content.BroadcastReceiver { + static final IntentFilter sPackageFilt = new IntentFilter(); + static final IntentFilter sNonDataFilt = new IntentFilter(); + static final IntentFilter sExternalFilt = new IntentFilter(); + + static { + sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); + sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); + sPackageFilt.addAction(Intent.ACTION_UID_REMOVED); + sPackageFilt.addDataScheme("package"); + sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED); + sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + } + + final HashSet<String> mUpdatingPackages = new HashSet<String>(); + + Context mRegisteredContext; + String[] mDisappearingPackages; + String[] mAppearingPackages; + String[] mModifiedPackages; + int mChangeType; + boolean mSomePackagesChanged; + + String[] mTempArray = new String[1]; + + public void register(Context context, boolean externalStorage) { + if (mRegisteredContext != null) { + throw new IllegalStateException("Already registered"); + } + mRegisteredContext = context; + context.registerReceiver(this, sPackageFilt); + context.registerReceiver(this, sNonDataFilt); + if (externalStorage) { + context.registerReceiver(this, sExternalFilt); + } + } + + public void unregister() { + if (mRegisteredContext == null) { + throw new IllegalStateException("Not registered"); + } + mRegisteredContext.unregisterReceiver(this); + mRegisteredContext = null; + } + + //not yet implemented + boolean isPackageUpdating(String packageName) { + synchronized (mUpdatingPackages) { + return mUpdatingPackages.contains(packageName); + } + } + + public void onBeginPackageChanges() { + } + + public void onPackageAdded(String packageName, int uid) { + } + + public void onPackageRemoved(String packageName, int uid) { + } + + public void onPackageUpdateStarted(String packageName, int uid) { + } + + public void onPackageUpdateFinished(String packageName, int uid) { + } + + public void onPackageChanged(String packageName, int uid, String[] components) { + } + + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { + return false; + } + + public void onUidRemoved(int uid) { + } + + public void onPackagesAvailable(String[] packages) { + } + + public void onPackagesUnavailable(String[] packages) { + } + + public static final int PACKAGE_UNCHANGED = 0; + public static final int PACKAGE_UPDATING = 1; + public static final int PACKAGE_TEMPORARY_CHANGE = 2; + public static final int PACKAGE_PERMANENT_CHANGE = 3; + + public void onPackageDisappeared(String packageName, int reason) { + } + + public void onPackageAppeared(String packageName, int reason) { + } + + public void onPackageModified(String packageName) { + } + + public boolean didSomePackagesChange() { + return mSomePackagesChanged; + } + + public int isPackageAppearing(String packageName) { + if (mAppearingPackages != null) { + for (int i=mAppearingPackages.length-1; i>=0; i--) { + if (packageName.equals(mAppearingPackages[i])) { + return mChangeType; + } + } + } + return PACKAGE_UNCHANGED; + } + + public boolean anyPackagesAppearing() { + return mAppearingPackages != null; + } + + public int isPackageDisappearing(String packageName) { + if (mDisappearingPackages != null) { + for (int i=mDisappearingPackages.length-1; i>=0; i--) { + if (packageName.equals(mDisappearingPackages[i])) { + return mChangeType; + } + } + } + return PACKAGE_UNCHANGED; + } + + public boolean anyPackagesDisappearing() { + return mDisappearingPackages != null; + } + + public boolean isPackageModified(String packageName) { + if (mModifiedPackages != null) { + for (int i=mModifiedPackages.length-1; i>=0; i--) { + if (packageName.equals(mModifiedPackages[i])) { + return true; + } + } + } + return false; + } + + public void onSomePackagesChanged() { + } + + public void onFinishPackageChanges() { + } + + String getPackageName(Intent intent) { + Uri uri = intent.getData(); + String pkg = uri != null ? uri.getSchemeSpecificPart() : null; + return pkg; + } + + @Override + public void onReceive(Context context, Intent intent) { + onBeginPackageChanges(); + + mDisappearingPackages = mAppearingPackages = null; + mSomePackagesChanged = false; + + String action = intent.getAction(); + if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + // We consider something to have changed regardless of whether + // this is just an update, because the update is now finished + // and the contents of the package may have changed. + mSomePackagesChanged = true; + if (pkg != null) { + mAppearingPackages = mTempArray; + mTempArray[0] = pkg; + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mModifiedPackages = mTempArray; + mChangeType = PACKAGE_UPDATING; + onPackageUpdateFinished(pkg, uid); + onPackageModified(pkg); + } else { + mChangeType = PACKAGE_PERMANENT_CHANGE; + onPackageAdded(pkg, uid); + } + onPackageAppeared(pkg, mChangeType); + if (mChangeType == PACKAGE_UPDATING) { + synchronized (mUpdatingPackages) { + mUpdatingPackages.remove(pkg); + } + } + } + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + if (pkg != null) { + mDisappearingPackages = mTempArray; + mTempArray[0] = pkg; + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + mChangeType = PACKAGE_UPDATING; + synchronized (mUpdatingPackages) { + //not used for now + //mUpdatingPackages.add(pkg); + } + onPackageUpdateStarted(pkg, uid); + } else { + mChangeType = PACKAGE_PERMANENT_CHANGE; + // We only consider something to have changed if this is + // not a replace; for a replace, we just need to consider + // it when it is re-added. + mSomePackagesChanged = true; + onPackageRemoved(pkg, uid); + } + onPackageDisappeared(pkg, mChangeType); + } + } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + String pkg = getPackageName(intent); + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + String[] components = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); + if (pkg != null) { + mModifiedPackages = mTempArray; + mTempArray[0] = pkg; + onPackageChanged(pkg, uid, components); + // XXX Don't want this to always cause mSomePackagesChanged, + // since it can happen a fair amount. + onPackageModified(pkg); + } + } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { + mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); + mChangeType = PACKAGE_TEMPORARY_CHANGE; + boolean canRestart = onHandleForceStop(intent, + mDisappearingPackages, + intent.getIntExtra(Intent.EXTRA_UID, 0), false); + if (canRestart) setResultCode(Activity.RESULT_OK); + } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { + mDisappearingPackages = new String[] {getPackageName(intent)}; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + onHandleForceStop(intent, mDisappearingPackages, + intent.getIntExtra(Intent.EXTRA_UID, 0), true); + } else if (Intent.ACTION_UID_REMOVED.equals(action)) { + onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0)); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + mAppearingPackages = pkgList; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + mSomePackagesChanged = true; + if (pkgList != null) { + onPackagesAvailable(pkgList); + for (int i=0; i<pkgList.length; i++) { + onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + } + } + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { + String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + mDisappearingPackages = pkgList; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + mSomePackagesChanged = true; + if (pkgList != null) { + onPackagesUnavailable(pkgList); + for (int i=0; i<pkgList.length; i++) { + onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + } + } + } + + if (mSomePackagesChanged) { + onSomePackagesChanged(); + } + + onFinishPackageChanges(); + } +} |