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.annotation.NonNull;
20 import android.app.Activity;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.UserHandle;
29 import android.util.Slog;
30 
31 import com.android.internal.os.BackgroundThread;
32 
33 import java.util.HashSet;
34 import java.util.Objects;
35 
36 /**
37  * Helper class for monitoring the state of packages: adding, removing,
38  * updating, and disappearing and reappearing on the SD card.
39  */
40 public abstract class PackageMonitor extends android.content.BroadcastReceiver {
41     static final String TAG = "PackageMonitor";
42 
43     final IntentFilter mPackageFilt;
44     final IntentFilter mNonDataFilt;
45     final IntentFilter mExternalFilt;
46 
47     final HashSet<String> mUpdatingPackages = new HashSet<String>();
48 
49     Context mRegisteredContext;
50     Handler mRegisteredHandler;
51     String[] mDisappearingPackages;
52     String[] mAppearingPackages;
53     String[] mModifiedPackages;
54     int mChangeType;
55     int mChangeUserId = UserHandle.USER_NULL;
56     boolean mSomePackagesChanged;
57     String[] mModifiedComponents;
58 
59     String[] mTempArray = new String[1];
60 
61     @UnsupportedAppUsage
PackageMonitor()62     public PackageMonitor() {
63         final boolean isCore = UserHandle.isCore(android.os.Process.myUid());
64 
65         mPackageFilt = new IntentFilter();
66         mPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
67         mPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
68         mPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
69         mPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
70         mPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
71         mPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
72         mPackageFilt.addDataScheme("package");
73         if (isCore) {
74             mPackageFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
75         }
76 
77         mNonDataFilt = new IntentFilter();
78         mNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
79         mNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
80         mNonDataFilt.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
81         mNonDataFilt.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
82         if (isCore) {
83             mNonDataFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
84         }
85 
86         mExternalFilt = new IntentFilter();
87         mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
88         mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
89         if (isCore) {
90             mExternalFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
91         }
92     }
93 
94     @UnsupportedAppUsage
register(Context context, Looper thread, boolean externalStorage)95     public void register(Context context, Looper thread, boolean externalStorage) {
96         register(context, thread, null, externalStorage);
97     }
98 
99     @UnsupportedAppUsage
register(Context context, Looper thread, UserHandle user, boolean externalStorage)100     public void register(Context context, Looper thread, UserHandle user,
101             boolean externalStorage) {
102         register(context, user, externalStorage,
103                 (thread == null) ? BackgroundThread.getHandler() : new Handler(thread));
104     }
105 
register(Context context, UserHandle user, boolean externalStorage, Handler handler)106     public void register(Context context, UserHandle user,
107         boolean externalStorage, Handler handler) {
108         if (mRegisteredContext != null) {
109             throw new IllegalStateException("Already registered");
110         }
111         mRegisteredContext = context;
112         mRegisteredHandler = Objects.requireNonNull(handler);
113         if (user != null) {
114             context.registerReceiverAsUser(this, user, mPackageFilt, null, mRegisteredHandler);
115             context.registerReceiverAsUser(this, user, mNonDataFilt, null, mRegisteredHandler);
116             if (externalStorage) {
117                 context.registerReceiverAsUser(this, user, mExternalFilt, null,
118                         mRegisteredHandler);
119             }
120         } else {
121             context.registerReceiver(this, mPackageFilt, null, mRegisteredHandler);
122             context.registerReceiver(this, mNonDataFilt, null, mRegisteredHandler);
123             if (externalStorage) {
124                 context.registerReceiver(this, mExternalFilt, null, mRegisteredHandler);
125             }
126         }
127     }
128 
getRegisteredHandler()129     public Handler getRegisteredHandler() {
130         return mRegisteredHandler;
131     }
132 
133     @UnsupportedAppUsage
unregister()134     public void unregister() {
135         if (mRegisteredContext == null) {
136             throw new IllegalStateException("Not registered");
137         }
138         mRegisteredContext.unregisterReceiver(this);
139         mRegisteredContext = null;
140     }
141 
142     //not yet implemented
isPackageUpdating(String packageName)143     boolean isPackageUpdating(String packageName) {
144         synchronized (mUpdatingPackages) {
145             return mUpdatingPackages.contains(packageName);
146         }
147     }
148 
onBeginPackageChanges()149     public void onBeginPackageChanges() {
150     }
151 
152     /**
153      * Called when a package is really added (and not replaced).
154      */
onPackageAdded(String packageName, int uid)155     public void onPackageAdded(String packageName, int uid) {
156     }
157 
158     /**
159      * Called when a package is really removed (and not replaced).
160      */
161     @UnsupportedAppUsage
onPackageRemoved(String packageName, int uid)162     public void onPackageRemoved(String packageName, int uid) {
163     }
164 
165     /**
166      * Called when a package is really removed (and not replaced) for
167      * all users on the device.
168      */
onPackageRemovedAllUsers(String packageName, int uid)169     public void onPackageRemovedAllUsers(String packageName, int uid) {
170     }
171 
onPackageUpdateStarted(String packageName, int uid)172     public void onPackageUpdateStarted(String packageName, int uid) {
173     }
174 
onPackageUpdateFinished(String packageName, int uid)175     public void onPackageUpdateFinished(String packageName, int uid) {
176     }
177 
178     /**
179      * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
180      * Intent.ACTION_PACKAGE_CHANGED} being received, informing you of
181      * changes to the enabled/disabled state of components in a package
182      * and/or of the overall package.
183      *
184      * @param packageName The name of the package that is changing.
185      * @param uid The user ID the package runs under.
186      * @param components Any components in the package that are changing.  If
187      * the overall package is changing, this will contain an entry of the
188      * package name itself.
189      * @return Return true to indicate you care about this change, which will
190      * result in {@link #onSomePackagesChanged()} being called later.  If you
191      * return false, no further callbacks will happen about this change.  The
192      * default implementation returns true if this is a change to the entire
193      * package.
194      */
195     @UnsupportedAppUsage
onPackageChanged(String packageName, int uid, String[] components)196     public boolean onPackageChanged(String packageName, int uid, String[] components) {
197         if (components != null) {
198             for (String name : components) {
199                 if (packageName.equals(name)) {
200                     return true;
201                 }
202             }
203         }
204         return false;
205     }
206 
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)207     public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
208         return false;
209     }
210 
onHandleUserStop(Intent intent, int userHandle)211     public void onHandleUserStop(Intent intent, int userHandle) {
212     }
213 
onUidRemoved(int uid)214     public void onUidRemoved(int uid) {
215     }
216 
onPackagesAvailable(String[] packages)217     public void onPackagesAvailable(String[] packages) {
218     }
219 
onPackagesUnavailable(String[] packages)220     public void onPackagesUnavailable(String[] packages) {
221     }
222 
onPackagesSuspended(String[] packages)223     public void onPackagesSuspended(String[] packages) {
224     }
225 
onPackagesUnsuspended(String[] packages)226     public void onPackagesUnsuspended(String[] packages) {
227     }
228 
229     public static final int PACKAGE_UNCHANGED = 0;
230     public static final int PACKAGE_UPDATING = 1;
231     public static final int PACKAGE_TEMPORARY_CHANGE = 2;
232     public static final int PACKAGE_PERMANENT_CHANGE = 3;
233 
234     /**
235      * Called when a package disappears for any reason.
236      */
onPackageDisappeared(String packageName, int reason)237     public void onPackageDisappeared(String packageName, int reason) {
238     }
239 
240     /**
241      * Called when a package appears for any reason.
242      */
onPackageAppeared(String packageName, int reason)243     public void onPackageAppeared(String packageName, int reason) {
244     }
245 
246     /**
247      * Called when an existing package is updated or its disabled state changes.
248      */
onPackageModified(@onNull String packageName)249     public void onPackageModified(@NonNull String packageName) {
250     }
251 
didSomePackagesChange()252     public boolean didSomePackagesChange() {
253         return mSomePackagesChanged;
254     }
255 
isPackageAppearing(String packageName)256     public int isPackageAppearing(String packageName) {
257         if (mAppearingPackages != null) {
258             for (int i=mAppearingPackages.length-1; i>=0; i--) {
259                 if (packageName.equals(mAppearingPackages[i])) {
260                     return mChangeType;
261                 }
262             }
263         }
264         return PACKAGE_UNCHANGED;
265     }
266 
anyPackagesAppearing()267     public boolean anyPackagesAppearing() {
268         return mAppearingPackages != null;
269     }
270 
271     @UnsupportedAppUsage
isPackageDisappearing(String packageName)272     public int isPackageDisappearing(String packageName) {
273         if (mDisappearingPackages != null) {
274             for (int i=mDisappearingPackages.length-1; i>=0; i--) {
275                 if (packageName.equals(mDisappearingPackages[i])) {
276                     return mChangeType;
277                 }
278             }
279         }
280         return PACKAGE_UNCHANGED;
281     }
282 
anyPackagesDisappearing()283     public boolean anyPackagesDisappearing() {
284         return mDisappearingPackages != null;
285     }
286 
isReplacing()287     public boolean isReplacing() {
288         return mChangeType == PACKAGE_UPDATING;
289     }
290 
291     @UnsupportedAppUsage
isPackageModified(String packageName)292     public boolean isPackageModified(String packageName) {
293         if (mModifiedPackages != null) {
294             for (int i=mModifiedPackages.length-1; i>=0; i--) {
295                 if (packageName.equals(mModifiedPackages[i])) {
296                     return true;
297                 }
298             }
299         }
300         return false;
301     }
302 
isComponentModified(String className)303     public boolean isComponentModified(String className) {
304         if (className == null || mModifiedComponents == null) {
305             return false;
306         }
307         for (int i = mModifiedComponents.length - 1; i >= 0; i--) {
308             if (className.equals(mModifiedComponents[i])) {
309                 return true;
310             }
311         }
312         return false;
313     }
314 
onSomePackagesChanged()315     public void onSomePackagesChanged() {
316     }
317 
onFinishPackageChanges()318     public void onFinishPackageChanges() {
319     }
320 
onPackageDataCleared(String packageName, int uid)321     public void onPackageDataCleared(String packageName, int uid) {
322     }
323 
324     /**
325      * Callback to indicate the package's state has changed.
326      * @param packageName Name of an installed package
327      * @param uid The UID the package runs under.
328      */
onPackageStateChanged(String packageName, int uid)329     public void onPackageStateChanged(String packageName, int uid) {}
330 
getChangingUserId()331     public int getChangingUserId() {
332         return mChangeUserId;
333     }
334 
getPackageName(Intent intent)335     String getPackageName(Intent intent) {
336         Uri uri = intent.getData();
337         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
338         return pkg;
339     }
340 
341     @Override
onReceive(Context context, Intent intent)342     public void onReceive(Context context, Intent intent) {
343         mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
344                 UserHandle.USER_NULL);
345         if (mChangeUserId == UserHandle.USER_NULL) {
346             Slog.w("PackageMonitor", "Intent broadcast does not contain user handle: " + intent);
347             return;
348         }
349         onBeginPackageChanges();
350 
351         mDisappearingPackages = mAppearingPackages = null;
352         mSomePackagesChanged = false;
353         mModifiedComponents = null;
354 
355         String action = intent.getAction();
356         if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
357             String pkg = getPackageName(intent);
358             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
359             // We consider something to have changed regardless of whether
360             // this is just an update, because the update is now finished
361             // and the contents of the package may have changed.
362             mSomePackagesChanged = true;
363             if (pkg != null) {
364                 mAppearingPackages = mTempArray;
365                 mTempArray[0] = pkg;
366                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
367                     mModifiedPackages = mTempArray;
368                     mChangeType = PACKAGE_UPDATING;
369                     onPackageUpdateFinished(pkg, uid);
370                     onPackageModified(pkg);
371                 } else {
372                     mChangeType = PACKAGE_PERMANENT_CHANGE;
373                     onPackageAdded(pkg, uid);
374                 }
375                 onPackageAppeared(pkg, mChangeType);
376                 if (mChangeType == PACKAGE_UPDATING) {
377                     synchronized (mUpdatingPackages) {
378                         mUpdatingPackages.remove(pkg);
379                     }
380                 }
381             }
382         } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
383             String pkg = getPackageName(intent);
384             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
385             if (pkg != null) {
386                 mDisappearingPackages = mTempArray;
387                 mTempArray[0] = pkg;
388                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
389                     mChangeType = PACKAGE_UPDATING;
390                     synchronized (mUpdatingPackages) {
391                         //not used for now
392                         //mUpdatingPackages.add(pkg);
393                     }
394                     onPackageUpdateStarted(pkg, uid);
395                 } else {
396                     mChangeType = PACKAGE_PERMANENT_CHANGE;
397                     // We only consider something to have changed if this is
398                     // not a replace; for a replace, we just need to consider
399                     // it when it is re-added.
400                     mSomePackagesChanged = true;
401                     onPackageRemoved(pkg, uid);
402                     if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) {
403                         onPackageRemovedAllUsers(pkg, uid);
404                     }
405                 }
406                 onPackageDisappeared(pkg, mChangeType);
407             }
408         } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
409             String pkg = getPackageName(intent);
410             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
411             mModifiedComponents = intent.getStringArrayExtra(
412                     Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
413             if (pkg != null) {
414                 mModifiedPackages = mTempArray;
415                 mTempArray[0] = pkg;
416                 mChangeType = PACKAGE_PERMANENT_CHANGE;
417                 if (onPackageChanged(pkg, uid, mModifiedComponents)) {
418                     mSomePackagesChanged = true;
419                 }
420                 onPackageModified(pkg);
421             }
422         } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
423             String pkg = getPackageName(intent);
424             int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
425             if (pkg != null) {
426                 onPackageDataCleared(pkg, uid);
427             }
428         } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
429             mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
430             mChangeType = PACKAGE_TEMPORARY_CHANGE;
431             boolean canRestart = onHandleForceStop(intent,
432                     mDisappearingPackages,
433                     intent.getIntExtra(Intent.EXTRA_UID, 0), false);
434             if (canRestart) setResultCode(Activity.RESULT_OK);
435         } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
436             mDisappearingPackages = new String[] {getPackageName(intent)};
437             mChangeType = PACKAGE_TEMPORARY_CHANGE;
438             onHandleForceStop(intent, mDisappearingPackages,
439                     intent.getIntExtra(Intent.EXTRA_UID, 0), true);
440         } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
441             onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
442         } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
443             if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
444                 onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
445             }
446         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
447             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
448             mAppearingPackages = pkgList;
449             mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
450                     ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
451             mSomePackagesChanged = true;
452             if (pkgList != null) {
453                 onPackagesAvailable(pkgList);
454                 for (int i=0; i<pkgList.length; i++) {
455                     onPackageAppeared(pkgList[i], mChangeType);
456                 }
457             }
458         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
459             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
460             mDisappearingPackages = pkgList;
461             mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
462                     ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE;
463             mSomePackagesChanged = true;
464             if (pkgList != null) {
465                 onPackagesUnavailable(pkgList);
466                 for (int i=0; i<pkgList.length; i++) {
467                     onPackageDisappeared(pkgList[i], mChangeType);
468                 }
469             }
470         } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) {
471             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
472             mSomePackagesChanged = true;
473             onPackagesSuspended(pkgList);
474         } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) {
475             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
476             mSomePackagesChanged = true;
477             onPackagesUnsuspended(pkgList);
478         }
479 
480         if (mSomePackagesChanged) {
481             onSomePackagesChanged();
482         }
483 
484         onFinishPackageChanges();
485         mChangeUserId = UserHandle.USER_NULL;
486     }
487 }
488