1 /*
2  * Copyright (C) 2019 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 android.telephony;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.BroadcastReceiver;
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.HandlerThread;
28 import android.os.Looper;
29 import android.os.UserHandle;
30 
31 /**
32  * Helper class for monitoring the state of packages: adding, removing,
33  * updating, and disappearing and reappearing on the SD card.
34  */
35 public abstract class PackageChangeReceiver extends BroadcastReceiver {
36     static final IntentFilter sPackageIntentFilter = new IntentFilter();
37     private static HandlerThread sHandlerThread;
38     static {
39         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
40         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
41         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
42         sPackageIntentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
43         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
44         sPackageIntentFilter.addDataScheme("package");
45     }
46     // Keep an instance of Context around as long as we still want the receiver:
47     // if the instance of Context gets garbage-collected, it'll unregister the receiver, so only
48     // unset when we want to unregister.
49     Context mRegisteredContext;
50 
51     /**
52      * To register the intents that needed for monitoring the state of packages. Once this method
53      * has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must
54      * have the same {@code user} argument.
55      */
register(@onNull Context context, @Nullable Looper thread, @Nullable UserHandle user)56     public void register(@NonNull Context context, @Nullable Looper thread,
57             @Nullable UserHandle user) {
58         if (mRegisteredContext != null) {
59             throw new IllegalStateException("Already registered");
60         }
61         Handler handler = new Handler(thread == null ? getStaticLooper() : thread);
62         mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0);
63         mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler);
64     }
65 
66     /**
67      * To unregister the intents for monitoring the state of packages
68      */
unregister()69     public void unregister() {
70         if (mRegisteredContext == null) {
71             throw new IllegalStateException("Not registered");
72         }
73         mRegisteredContext.unregisterReceiver(this);
74         mRegisteredContext = null;
75     }
76 
getStaticLooper()77     private static synchronized Looper getStaticLooper() {
78         if (sHandlerThread == null) {
79             sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName());
80             sHandlerThread.start();
81         }
82         return sHandlerThread.getLooper();
83     }
84 
85     /**
86      * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
87      */
onPackageAdded(@ullable String packageName)88     public void onPackageAdded(@Nullable String packageName) {
89     }
90 
91     /**
92      * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED
93      */
onPackageRemoved(@ullable String packageName)94     public void onPackageRemoved(@Nullable String packageName) {
95     }
96 
97     /**
98      * This method is invoked when Intent.EXTRA_REPLACING as extra field is true
99      */
onPackageUpdateFinished(@ullable String packageName)100     public void onPackageUpdateFinished(@Nullable String packageName) {
101     }
102 
103     /**
104      * This method is invoked when receive the Intent.ACTION_PACKAGE_CHANGED or
105      * Intent.EXTRA_REPLACING as extra field is true
106      */
onPackageModified(@ullable String packageName)107     public void onPackageModified(@Nullable String packageName) {
108     }
109 
110     /**
111      * This method is invoked when receive the Intent.ACTION_QUERY_PACKAGE_RESTART and
112      * Intent.ACTION_PACKAGE_RESTARTED
113      */
onHandleForceStop(@ullable String[] packages, boolean doit)114     public void onHandleForceStop(@Nullable String[] packages, boolean doit) {
115     }
116 
117     /**
118      * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED
119      */
onPackageDisappeared()120     public void onPackageDisappeared() {
121     }
122 
123     /**
124      * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
125      */
onPackageAppeared()126     public void onPackageAppeared() {
127     }
128 
129     @Override
onReceive(@ullable Context context, @Nullable Intent intent)130     public void onReceive(@Nullable Context context, @Nullable Intent intent) {
131         String action = intent.getAction();
132 
133         if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
134             String pkg = getPackageName(intent);
135             if (pkg != null) {
136                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
137                     onPackageUpdateFinished(pkg);
138                     onPackageModified(pkg);
139                 } else {
140                     onPackageAdded(pkg);
141                 }
142                 onPackageAppeared();
143             }
144         } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
145             String pkg = getPackageName(intent);
146             if (pkg != null) {
147                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
148                     onPackageRemoved(pkg);
149                 }
150                 onPackageDisappeared();
151             }
152         } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
153             String pkg = getPackageName(intent);
154             if (pkg != null) {
155                 onPackageModified(pkg);
156             }
157         } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
158             String[] disappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
159             onHandleForceStop(disappearingPackages, false);
160         } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
161             String[] disappearingPackages = new String[] {getPackageName(intent)};
162             onHandleForceStop(disappearingPackages, true);
163         }
164     }
165 
getPackageName(Intent intent)166     String getPackageName(Intent intent) {
167         Uri uri = intent.getData();
168         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
169         return pkg;
170     }
171 }
172