blob: 31ca3dee8c09222ccbdfd1abf22c70c92308ea6b [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;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070025import android.os.Looper;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070026import android.os.UserHandle;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070027import com.android.internal.os.BackgroundThread;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080028
29import 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 */
35public 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();
Dianne Hackbornd0d75032012-04-19 23:12:09 -070039
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080040 static {
41 sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
42 sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
43 sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
44 sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
45 sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
46 sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
47 sPackageFilt.addDataScheme("package");
48 sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
Dianne Hackborn80a4af22012-08-27 19:18:31 -070049 sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080050 sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
51 sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
52 }
53
54 final HashSet<String> mUpdatingPackages = new HashSet<String>();
55
56 Context mRegisteredContext;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070057 Handler mRegisteredHandler;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080058 String[] mDisappearingPackages;
59 String[] mAppearingPackages;
60 String[] mModifiedPackages;
61 int mChangeType;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070062 int mChangeUserId = UserHandle.USER_NULL;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080063 boolean mSomePackagesChanged;
Dianne Hackbornc72fc672012-09-20 13:12:03 -070064
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080065 String[] mTempArray = new String[1];
Dianne Hackbornc72fc672012-09-20 13:12:03 -070066
Dianne Hackbornd0d75032012-04-19 23:12:09 -070067 public void register(Context context, Looper thread, boolean externalStorage) {
Dianne Hackbornc72fc672012-09-20 13:12:03 -070068 register(context, thread, null, externalStorage);
69 }
70
71 public void register(Context context, Looper thread, UserHandle user,
72 boolean externalStorage) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080073 if (mRegisteredContext != null) {
74 throw new IllegalStateException("Already registered");
75 }
76 mRegisteredContext = context;
Dianne Hackbornd0d75032012-04-19 23:12:09 -070077 if (thread == null) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -070078 mRegisteredHandler = BackgroundThread.getHandler();
Dianne Hackbornd0d75032012-04-19 23:12:09 -070079 } else {
80 mRegisteredHandler = new Handler(thread);
81 }
Dianne Hackbornc72fc672012-09-20 13:12:03 -070082 if (user != null) {
83 context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler);
84 context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler);
85 if (externalStorage) {
86 context.registerReceiverAsUser(this, user, sExternalFilt, null,
87 mRegisteredHandler);
88 }
89 } else {
90 context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler);
91 context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler);
92 if (externalStorage) {
93 context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler);
94 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080095 }
96 }
Dianne Hackbornd0d75032012-04-19 23:12:09 -070097
98 public Handler getRegisteredHandler() {
99 return mRegisteredHandler;
100 }
101
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800102 public void unregister() {
103 if (mRegisteredContext == null) {
104 throw new IllegalStateException("Not registered");
105 }
106 mRegisteredContext.unregisterReceiver(this);
107 mRegisteredContext = null;
108 }
109
110 //not yet implemented
111 boolean isPackageUpdating(String packageName) {
112 synchronized (mUpdatingPackages) {
113 return mUpdatingPackages.contains(packageName);
114 }
115 }
116
117 public void onBeginPackageChanges() {
118 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700119
120 /**
121 * Called when a package is really added (and not replaced).
122 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800123 public void onPackageAdded(String packageName, int uid) {
124 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700125
126 /**
127 * Called when a package is really removed (and not replaced).
128 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800129 public void onPackageRemoved(String packageName, int uid) {
130 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700131
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700132 /**
133 * Called when a package is really removed (and not replaced) for
134 * all users on the device.
135 */
136 public void onPackageRemovedAllUsers(String packageName, int uid) {
137 }
138
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800139 public void onPackageUpdateStarted(String packageName, int uid) {
140 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700141
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800142 public void onPackageUpdateFinished(String packageName, int uid) {
143 }
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800144
145 /**
146 * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
147 * Intent.ACTION_PACKAGE_CHANGED} being received, informing you of
148 * changes to the enabled/disabled state of components in a package
149 * and/or of the overall package.
150 *
151 * @param packageName The name of the package that is changing.
152 * @param uid The user ID the package runs under.
153 * @param components Any components in the package that are changing. If
154 * the overall package is changing, this will contain an entry of the
155 * package name itself.
156 * @return Return true to indicate you care about this change, which will
157 * result in {@link #onSomePackagesChanged()} being called later. If you
158 * return false, no further callbacks will happen about this change. The
159 * default implementation returns true if this is a change to the entire
160 * package.
161 */
162 public boolean onPackageChanged(String packageName, int uid, String[] components) {
163 if (components != null) {
164 for (String name : components) {
165 if (packageName.equals(name)) {
166 return true;
167 }
168 }
169 }
170 return false;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800171 }
172
173 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
174 return false;
175 }
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700176
177 public void onHandleUserStop(Intent intent, int userHandle) {
178 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800179
180 public void onUidRemoved(int uid) {
181 }
182
183 public void onPackagesAvailable(String[] packages) {
184 }
185
186 public void onPackagesUnavailable(String[] packages) {
187 }
188
189 public static final int PACKAGE_UNCHANGED = 0;
190 public static final int PACKAGE_UPDATING = 1;
191 public static final int PACKAGE_TEMPORARY_CHANGE = 2;
192 public static final int PACKAGE_PERMANENT_CHANGE = 3;
Nick Pelly357d9cb2012-07-13 21:32:34 -0700193
194 /**
195 * Called when a package disappears for any reason.
196 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800197 public void onPackageDisappeared(String packageName, int reason) {
198 }
Nick Pelly357d9cb2012-07-13 21:32:34 -0700199
200 /**
201 * Called when a package appears for any reason.
202 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800203 public void onPackageAppeared(String packageName, int reason) {
204 }
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800205
206 /**
207 * Called when an existing package is updated or its disabled state changes.
208 */
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800209 public void onPackageModified(String packageName) {
210 }
211
212 public boolean didSomePackagesChange() {
213 return mSomePackagesChanged;
214 }
215
216 public int isPackageAppearing(String packageName) {
217 if (mAppearingPackages != null) {
218 for (int i=mAppearingPackages.length-1; i>=0; i--) {
219 if (packageName.equals(mAppearingPackages[i])) {
220 return mChangeType;
221 }
222 }
223 }
224 return PACKAGE_UNCHANGED;
225 }
226
227 public boolean anyPackagesAppearing() {
228 return mAppearingPackages != null;
229 }
230
231 public int isPackageDisappearing(String packageName) {
232 if (mDisappearingPackages != null) {
233 for (int i=mDisappearingPackages.length-1; i>=0; i--) {
234 if (packageName.equals(mDisappearingPackages[i])) {
235 return mChangeType;
236 }
237 }
238 }
239 return PACKAGE_UNCHANGED;
240 }
241
242 public boolean anyPackagesDisappearing() {
243 return mDisappearingPackages != null;
244 }
245
246 public boolean isPackageModified(String packageName) {
247 if (mModifiedPackages != null) {
248 for (int i=mModifiedPackages.length-1; i>=0; i--) {
249 if (packageName.equals(mModifiedPackages[i])) {
250 return true;
251 }
252 }
253 }
254 return false;
255 }
256
257 public void onSomePackagesChanged() {
258 }
259
260 public void onFinishPackageChanges() {
261 }
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700262
263 public int getChangingUserId() {
264 return mChangeUserId;
265 }
266
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800267 String getPackageName(Intent intent) {
268 Uri uri = intent.getData();
269 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
270 return pkg;
271 }
272
273 @Override
274 public void onReceive(Context context, Intent intent) {
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700275 mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
276 UserHandle.USER_NULL);
277 if (mChangeUserId == UserHandle.USER_NULL) {
278 throw new IllegalArgumentException(
279 "Intent broadcast does not contain user handle: " + intent);
280 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800281 onBeginPackageChanges();
282
283 mDisappearingPackages = mAppearingPackages = null;
284 mSomePackagesChanged = false;
285
286 String action = intent.getAction();
287 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
288 String pkg = getPackageName(intent);
289 int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
290 // We consider something to have changed regardless of whether
291 // this is just an update, because the update is now finished
292 // and the contents of the package may have changed.
293 mSomePackagesChanged = true;
294 if (pkg != null) {
295 mAppearingPackages = mTempArray;
296 mTempArray[0] = pkg;
297 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
298 mModifiedPackages = mTempArray;
299 mChangeType = PACKAGE_UPDATING;
300 onPackageUpdateFinished(pkg, uid);
301 onPackageModified(pkg);
302 } else {
303 mChangeType = PACKAGE_PERMANENT_CHANGE;
304 onPackageAdded(pkg, uid);
305 }
306 onPackageAppeared(pkg, mChangeType);
307 if (mChangeType == PACKAGE_UPDATING) {
308 synchronized (mUpdatingPackages) {
309 mUpdatingPackages.remove(pkg);
310 }
311 }
312 }
313 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
314 String pkg = getPackageName(intent);
315 int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
316 if (pkg != null) {
317 mDisappearingPackages = mTempArray;
318 mTempArray[0] = pkg;
319 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
320 mChangeType = PACKAGE_UPDATING;
321 synchronized (mUpdatingPackages) {
322 //not used for now
323 //mUpdatingPackages.add(pkg);
324 }
325 onPackageUpdateStarted(pkg, uid);
326 } else {
327 mChangeType = PACKAGE_PERMANENT_CHANGE;
328 // We only consider something to have changed if this is
329 // not a replace; for a replace, we just need to consider
330 // it when it is re-added.
331 mSomePackagesChanged = true;
332 onPackageRemoved(pkg, uid);
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700333 if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) {
334 onPackageRemovedAllUsers(pkg, uid);
335 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800336 }
337 onPackageDisappeared(pkg, mChangeType);
338 }
339 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
340 String pkg = getPackageName(intent);
341 int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
342 String[] components = intent.getStringArrayExtra(
343 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
344 if (pkg != null) {
345 mModifiedPackages = mTempArray;
346 mTempArray[0] = pkg;
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800347 mChangeType = PACKAGE_PERMANENT_CHANGE;
348 if (onPackageChanged(pkg, uid, components)) {
349 mSomePackagesChanged = true;
350 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800351 onPackageModified(pkg);
352 }
353 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
354 mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
355 mChangeType = PACKAGE_TEMPORARY_CHANGE;
356 boolean canRestart = onHandleForceStop(intent,
357 mDisappearingPackages,
358 intent.getIntExtra(Intent.EXTRA_UID, 0), false);
359 if (canRestart) setResultCode(Activity.RESULT_OK);
360 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
361 mDisappearingPackages = new String[] {getPackageName(intent)};
362 mChangeType = PACKAGE_TEMPORARY_CHANGE;
363 onHandleForceStop(intent, mDisappearingPackages,
364 intent.getIntExtra(Intent.EXTRA_UID, 0), true);
365 } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
366 onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700367 } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
368 if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
369 onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
370 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800371 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
372 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
373 mAppearingPackages = pkgList;
Christopher Tate8b3e3ec2014-01-08 16:54:25 -0800374 mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
375 ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800376 mSomePackagesChanged = true;
377 if (pkgList != null) {
378 onPackagesAvailable(pkgList);
379 for (int i=0; i<pkgList.length; i++) {
Christopher Tate8b3e3ec2014-01-08 16:54:25 -0800380 onPackageAppeared(pkgList[i], mChangeType);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800381 }
382 }
383 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
384 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
385 mDisappearingPackages = pkgList;
Christopher Tate8b3e3ec2014-01-08 16:54:25 -0800386 mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
387 ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800388 mSomePackagesChanged = true;
389 if (pkgList != null) {
390 onPackagesUnavailable(pkgList);
391 for (int i=0; i<pkgList.length; i++) {
Christopher Tate8b3e3ec2014-01-08 16:54:25 -0800392 onPackageDisappeared(pkgList[i], mChangeType);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800393 }
394 }
395 }
396
397 if (mSomePackagesChanged) {
398 onSomePackagesChanged();
399 }
400
401 onFinishPackageChanges();
Dianne Hackbornc72fc672012-09-20 13:12:03 -0700402 mChangeUserId = UserHandle.USER_NULL;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800403 }
404}