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