blob: ab871fb80a59da39f36e68ba4abb8a7377eb11bc [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
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
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080017package com.android.internal.content;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.Uri;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070024import android.os.Handler;
25import android.os.HandlerThread;
26import android.os.Looper;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070027import android.os.UserHandle;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070028import com.android.internal.os.BackgroundThread;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080029
30import 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 */
36public 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();
Dianne Hackbornd0d75032012-04-19 23:12:09 -070040
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080041 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);
Dianne Hackborn80a4af22012-08-27 19:18:31 -070050 sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080051 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;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070058 Handler mRegisteredHandler;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080059 String[] mDisappearingPackages;
60 String[] mAppearingPackages;
61 String[] mModifiedPackages;
62 int mChangeType;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070063 int mChangeUserId = UserHandle.USER_NULL;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080064 boolean mSomePackagesChanged;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070065
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080066 String[] mTempArray = new String[1];
Dianne Hackbornc72fc672012-09-20 13:12:03 -070067
Dianne Hackbornd0d75032012-04-19 23:12:09 -070068 public void register(Context context, Looper thread, boolean externalStorage) {
Dianne Hackbornc72fc672012-09-20 13:12:03 -070069 register(context, thread, null, externalStorage);
70 }
71
72 public void register(Context context, Looper thread, UserHandle user,
73 boolean externalStorage) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080074 if (mRegisteredContext != null) {
75 throw new IllegalStateException("Already registered");
76 }
77 mRegisteredContext = context;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070078 if (thread == null) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -070079 mRegisteredHandler = BackgroundThread.getHandler();
Dianne Hackbornd0d75032012-04-19 23:12:09 -070080 } else {
81 mRegisteredHandler = new Handler(thread);
82 }
Dianne Hackbornc72fc672012-09-20 13:12:03 -070083 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 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080096 }
97 }
Dianne Hackbornd0d75032012-04-19 23:12:09 -070098
99 public Handler getRegisteredHandler() {
100 return mRegisteredHandler;
101 }
102
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800103 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 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700120
121 /**
122 * Called when a package is really added (and not replaced).
123 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800124 public void onPackageAdded(String packageName, int uid) {
125 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700126
127 /**
128 * Called when a package is really removed (and not replaced).
129 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800130 public void onPackageRemoved(String packageName, int uid) {
131 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700132
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700133 /**
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
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800140 public void onPackageUpdateStarted(String packageName, int uid) {
141 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700142
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800143 public void onPackageUpdateFinished(String packageName, int uid) {
144 }
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800145
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;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800172 }
173
174 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
175 return false;
176 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700177
178 public void onHandleUserStop(Intent intent, int userHandle) {
179 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800180
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;
Nick Pelly357d9cb2012-07-13 21:32:34 -0700194
195 /**
196 * Called when a package disappears for any reason.
197 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800198 public void onPackageDisappeared(String packageName, int reason) {
199 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700200
201 /**
202 * Called when a package appears for any reason.
203 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800204 public void onPackageAppeared(String packageName, int reason) {
205 }
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800206
207 /**
208 * Called when an existing package is updated or its disabled state changes.
209 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800210 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 }
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700263
264 public int getChangingUserId() {
265 return mChangeUserId;
266 }
267
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800268 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) {
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700276 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 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800282 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);
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700334 if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) {
335 onPackageRemovedAllUsers(pkg, uid);
336 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800337 }
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;
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800348 mChangeType = PACKAGE_PERMANENT_CHANGE;
349 if (onPackageChanged(pkg, uid, components)) {
350 mSomePackagesChanged = true;
351 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800352 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));
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700368 } 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 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800372 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
373 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
374 mAppearingPackages = pkgList;
375 mChangeType = PACKAGE_TEMPORARY_CHANGE;
376 mSomePackagesChanged = true;
377 if (pkgList != null) {
378 onPackagesAvailable(pkgList);
379 for (int i=0; i<pkgList.length; i++) {
380 onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
381 }
382 }
383 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
384 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
385 mDisappearingPackages = pkgList;
386 mChangeType = PACKAGE_TEMPORARY_CHANGE;
387 mSomePackagesChanged = true;
388 if (pkgList != null) {
389 onPackagesUnavailable(pkgList);
390 for (int i=0; i<pkgList.length; i++) {
391 onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
392 }
393 }
394 }
395
396 if (mSomePackagesChanged) {
397 onSomePackagesChanged();
398 }
399
400 onFinishPackageChanges();
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700401 mChangeUserId = UserHandle.USER_NULL;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800402 }
403}