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 com.android.internal.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