/* * 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; } }