| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.net.Uri; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.UserHandle; |
| |
| /** |
| * Helper class for monitoring the state of packages: adding, removing, |
| * updating, and disappearing and reappearing on the SD card. |
| */ |
| public abstract class PackageChangeReceiver extends BroadcastReceiver { |
| static final IntentFilter sPackageIntentFilter = new IntentFilter(); |
| private static HandlerThread sHandlerThread; |
| static { |
| sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); |
| sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); |
| sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); |
| sPackageIntentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); |
| sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); |
| sPackageIntentFilter.addDataScheme("package"); |
| } |
| // Keep an instance of Context around as long as we still want the receiver: |
| // if the instance of Context gets garbage-collected, it'll unregister the receiver, so only |
| // unset when we want to unregister. |
| Context mRegisteredContext; |
| |
| /** |
| * To register the intents that needed for monitoring the state of packages. Once this method |
| * has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must |
| * have the same {@code user} argument. |
| */ |
| public void register(@NonNull Context context, @Nullable Looper thread, |
| @Nullable UserHandle user) { |
| if (mRegisteredContext != null) { |
| throw new IllegalStateException("Already registered"); |
| } |
| Handler handler = new Handler(thread == null ? getStaticLooper() : thread); |
| mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0); |
| mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler); |
| } |
| |
| /** |
| * To unregister the intents for monitoring the state of packages |
| */ |
| public void unregister() { |
| if (mRegisteredContext == null) { |
| throw new IllegalStateException("Not registered"); |
| } |
| mRegisteredContext.unregisterReceiver(this); |
| mRegisteredContext = null; |
| } |
| |
| private static synchronized Looper getStaticLooper() { |
| if (sHandlerThread == null) { |
| sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName()); |
| sHandlerThread.start(); |
| } |
| return sHandlerThread.getLooper(); |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED |
| */ |
| public void onPackageAdded(@Nullable String packageName) { |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED |
| */ |
| public void onPackageRemoved(@Nullable String packageName) { |
| } |
| |
| /** |
| * This method is invoked when Intent.EXTRA_REPLACING as extra field is true |
| */ |
| public void onPackageUpdateFinished(@Nullable String packageName) { |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_PACKAGE_CHANGED or |
| * Intent.EXTRA_REPLACING as extra field is true |
| */ |
| public void onPackageModified(@Nullable String packageName) { |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_QUERY_PACKAGE_RESTART and |
| * Intent.ACTION_PACKAGE_RESTARTED |
| */ |
| public void onHandleForceStop(@Nullable String[] packages, boolean doit) { |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_PACKAGE_REMOVED |
| */ |
| public void onPackageDisappeared() { |
| } |
| |
| /** |
| * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED |
| */ |
| public void onPackageAppeared() { |
| } |
| |
| @Override |
| public void onReceive(@Nullable Context context, @Nullable Intent intent) { |
| String action = intent.getAction(); |
| |
| if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { |
| String pkg = getPackageName(intent); |
| if (pkg != null) { |
| if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { |
| onPackageUpdateFinished(pkg); |
| onPackageModified(pkg); |
| } else { |
| onPackageAdded(pkg); |
| } |
| onPackageAppeared(); |
| } |
| } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { |
| String pkg = getPackageName(intent); |
| if (pkg != null) { |
| if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { |
| onPackageRemoved(pkg); |
| } |
| onPackageDisappeared(); |
| } |
| } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { |
| String pkg = getPackageName(intent); |
| if (pkg != null) { |
| onPackageModified(pkg); |
| } |
| } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { |
| String[] disappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); |
| onHandleForceStop(disappearingPackages, false); |
| } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { |
| String[] disappearingPackages = new String[] {getPackageName(intent)}; |
| onHandleForceStop(disappearingPackages, true); |
| } |
| } |
| |
| String getPackageName(Intent intent) { |
| Uri uri = intent.getData(); |
| String pkg = uri != null ? uri.getSchemeSpecificPart() : null; |
| return pkg; |
| } |
| } |