Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f98512c authored by Annie Meng's avatar Annie Meng
Browse files

[Multi-user] Make package changes receiver per-user

Makes the BroadcastReceiver that receives changes to installed packages
and sdcards tied to a specific user.

Bug: 121198607
Test: 1) atest RunBackupFrameworksServicesRoboTests
2) Manual testing with user 0 and 11:
- User started -> receiver registered for correct user;
- Package changed -> correct receiver;
- Package added -> correct receiver, updates bookkeeping for correct user;
- Package removed -> correct receiver, updates bookkeeping for correct user;

Change-Id: I68c034da6ec775a4d0489a2d09fc32854dcf11dc
parent e714a7ea
Loading
Loading
Loading
Loading
+59 −46
Original line number Diff line number Diff line
@@ -830,19 +830,29 @@ public class UserBackupManagerService {
            mFullBackupQueue = readFullBackupSchedule();
        }

        // Register for broadcasts about package install, etc., so we can
        // update the provider list.
        // Register for broadcasts about package changes.
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);
        mContext.registerReceiverAsUser(
                mBroadcastReceiver,
                UserHandle.of(mUserId),
                filter,
                /* broadcastPermission */ null,
                /* scheduler */ null);

        // Register for events related to sdcard installation.
        IntentFilter sdFilter = new IntentFilter();
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
        mContext.registerReceiverAsUser(
                mBroadcastReceiver,
                UserHandle.of(mUserId),
                sdFilter,
                /* broadcastPermission */ null,
                /* scheduler */ null);
    }

    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
@@ -1107,17 +1117,23 @@ public class UserBackupManagerService {
        }
    }

    // ----- Track installation/removal of packages -----
    /**
     * A {@link BroadcastReceiver} tracking changes to packages and sd cards in order to update our
     * internal bookkeeping.
     */
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
            if (MORE_DEBUG) {
                Slog.d(TAG, "Received broadcast " + intent);
            }

            String action = intent.getAction();
            boolean replacing = false;
            boolean added = false;
            boolean changed = false;
            Bundle extras = intent.getExtras();
            String[] pkgList = null;
            String[] packageList = null;

            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -1125,69 +1141,70 @@ public class UserBackupManagerService {
                if (uri == null) {
                    return;
                }
                final String pkgName = uri.getSchemeSpecificPart();
                if (pkgName != null) {
                    pkgList = new String[]{pkgName};

                String packageName = uri.getSchemeSpecificPart();
                if (packageName != null) {
                    packageList = new String[]{packageName};
                }
                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);

                // At package-changed we only care about looking at new transport states
                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
                if (changed) {
                    final String[] components =
                    // Look at new transport states for package changed events.
                    String[] components =
                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);

                    if (MORE_DEBUG) {
                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
                        Slog.i(TAG, "Package " + packageName + " changed");
                        for (int i = 0; i < components.length; i++) {
                            Slog.i(TAG, "   * " + components[i]);
                        }
                    }

                    mBackupHandler.post(
                            () -> mTransportManager.onPackageChanged(pkgName, components));
                    return; // nothing more to do in the PACKAGE_CHANGED case
                            () -> mTransportManager.onPackageChanged(packageName, components));
                    return;
                }

                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                added = true;
                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                added = false;
                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
            }

            if (pkgList == null || pkgList.length == 0) {
            if (packageList == null || packageList.length == 0) {
                return;
            }

            final int uid = extras.getInt(Intent.EXTRA_UID);
            int uid = extras.getInt(Intent.EXTRA_UID);
            if (added) {
                synchronized (mBackupParticipants) {
                    if (replacing) {
                        // This is the package-replaced case; we just remove the entry
                        // under the old uid and fall through to re-add.  If an app
                        // just added key/value backup participation, this picks it up
                        // as a known participant.
                        removePackageParticipantsLocked(pkgList, uid);
                        // Remove the entry under the old uid and fall through to re-add. If an app
                        // just opted into key/value backup, add it as a known participant.
                        removePackageParticipantsLocked(packageList, uid);
                    }
                    addPackageParticipantsLocked(pkgList);
                    addPackageParticipantsLocked(packageList);
                }
                // If they're full-backup candidates, add them there instead
                final long now = System.currentTimeMillis();
                for (final String packageName : pkgList) {

                long now = System.currentTimeMillis();
                for (String packageName : packageList) {
                    try {
                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
                        PackageInfo app =
                                mPackageManager.getPackageInfoAsUser(
                                        packageName, /* flags */ 0, mUserId);
                        if (AppBackupUtils.appGetsFullBackup(app)
                                && AppBackupUtils.appIsEligibleForBackup(
                                app.applicationInfo, mPackageManager)) {
                            enqueueFullBackup(packageName, now);
                            scheduleNextFullBackupJob(0);
                        } else {
                            // The app might have just transitioned out of full-data into
                            // doing key/value backups, or might have just disabled backups
                            // entirely.  Make sure it is no longer in the full-data queue.
                            // The app might have just transitioned out of full-data into doing
                            // key/value backups, or might have just disabled backups entirely. Make
                            // sure it is no longer in the full-data queue.
                            synchronized (mQueueLock) {
                                dequeueFullBackupLocked(packageName);
                            }
@@ -1196,32 +1213,28 @@ public class UserBackupManagerService {

                        mBackupHandler.post(
                                () -> mTransportManager.onPackageAdded(packageName));

                    } catch (NameNotFoundException e) {
                        // doesn't really exist; ignore it
                        if (DEBUG) {
                            Slog.w(TAG, "Can't resolve new app " + packageName);
                        }
                    }
                }

                // Whenever a package is added or updated we need to update
                // the package metadata bookkeeping.
                // Whenever a package is added or updated we need to update the package metadata
                // bookkeeping.
                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
            } else {
                if (replacing) {
                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
                } else {
                    // Outright removal.  In the full-data case, the app will be dropped
                    // from the queue when its (now obsolete) name comes up again for
                    // backup.
                if (!replacing) {
                    // Outright removal. In the full-data case, the app will be dropped from the
                    // queue when its (now obsolete) name comes up again for backup.
                    synchronized (mBackupParticipants) {
                        removePackageParticipantsLocked(pkgList, uid);
                        removePackageParticipantsLocked(packageList, uid);
                    }
                }
                for (final String pkgName : pkgList) {

                for (String packageName : packageList) {
                    mBackupHandler.post(
                            () -> mTransportManager.onPackageRemoved(pkgName));
                            () -> mTransportManager.onPackageRemoved(packageName));
                }
            }
        }
+32 −1
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import static java.util.stream.Collectors.toList;

import android.annotation.Nullable;
import android.app.Application;
import android.app.ApplicationPackageManager;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -134,6 +135,8 @@ import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
@@ -157,6 +160,7 @@ import java.util.stream.Stream;
@Config(
        shadows = {
            FrameworkShadowLooper.class,
            KeyValueBackupTaskTest.ShadowApplicationPackageManager.class,
            ShadowBackupDataInput.class,
            ShadowBackupDataOutput.class,
            ShadowEventLog.class,
@@ -244,6 +248,7 @@ public class KeyValueBackupTaskTest {
    @After
    public void tearDown() throws Exception {
        ShadowBackupDataInput.reset();
        ShadowApplicationPackageManager.reset();
    }

    @Test
@@ -2435,7 +2440,8 @@ public class KeyValueBackupTaskTest {
            mPackageManager.setApplicationEnabledSetting(
                    packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
            PackageInfo packageInfo = getPackageInfo(packageData);
            mShadowPackageManager.addPackage(packageInfo);
            mShadowPackageManager.installPackage(packageInfo);
            ShadowApplicationPackageManager.setPackageInfo(packageInfo);
            mContext.sendBroadcast(getPackageAddedIntent(packageData));
            // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
            mShadowBackupLooper.runToEndOfTasks();
@@ -2848,4 +2854,29 @@ public class KeyValueBackupTaskTest {
            throw mException;
        }
    }

    /**
     * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
     * package in user-specific invocations.
     */
    @Implements(value = ApplicationPackageManager.class)
    public static class ShadowApplicationPackageManager
            extends org.robolectric.shadows.ShadowApplicationPackageManager {
        private static PackageInfo sPackageInfo;

        static void setPackageInfo(PackageInfo packageInfo) {
            sPackageInfo = packageInfo;
        }

        @Override
        protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) {
            return sPackageInfo;
        }

        /** Clear {@link #sPackageInfo}. */
        @Resetter
        public static void reset() {
            sPackageInfo = null;
        }
    }
}