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

Commit 2c936946 authored by Joshua Trask's avatar Joshua Trask Committed by Android (Google) Code Review
Browse files

Merge "Readability cleanup in AppPrediction comparator."

parents b2223708 8085ad18
Loading
Loading
Loading
Loading
+108 −89
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.internal.app;

import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;

import android.annotation.Nullable;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
@@ -34,6 +33,7 @@ import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -51,16 +51,17 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator

    private final AppPredictor mAppPredictor;
    private final Context mContext;
    private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>();
    private final Map<ComponentName, Integer> mTargetScores = new HashMap<>();
    private final UserHandle mUser;
    private final Intent mIntent;
    private final String mReferrerPackage;

    private final ModelBuilder mModelBuilder;
    private ResolverComparatorModel mComparatorModel;

    // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
    // back to using the ResolverRankerService.
    // TODO: responsibility for this fallback behavior can live outside of the AppPrediction client.
    private ResolverRankerServiceResolverComparator mResolverRankerService;
    private AppPredictionServiceComparatorModel mComparatorModel;

    AppPredictionServiceResolverComparator(
            Context context,
@@ -76,7 +77,20 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
        mUser = user;
        mReferrerPackage = referrerPackage;
        setChooserActivityLogger(chooserActivityLogger);
        mComparatorModel = buildUpdatedModel();

        mModelBuilder = new ModelBuilder(appPredictor, user);
        mComparatorModel = mModelBuilder.buildFromRankedList(Collections.emptyList());
    }

    @Override
    void destroy() {
        if (mResolverRankerService != null) {
            mResolverRankerService.destroy();
            mResolverRankerService = null;

            // TODO: may not be necessary to build a new model, since we're destroying anyways.
            mComparatorModel = mModelBuilder.buildFallbackModel(mResolverRankerService);
        }
    }

    @Override
@@ -84,6 +98,29 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
        return mComparatorModel.getComparator().compare(lhs, rhs);
    }

    @Override
    float getScore(ComponentName name) {
        return mComparatorModel.getScore(name);
    }

    @Override
    void updateModel(ComponentName componentName) {
        mComparatorModel.notifyOnTargetSelected(componentName);
    }

    @Override
    void handleResultMessage(Message msg) {
        // Null value is okay if we have defaulted to the ResolverRankerService.
        if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
            // TODO: this probably never happens? The sorting callback circumvents the Handler
            // design to call handleResult() directly instead of sending the list through a Message.
            // (OK to leave as-is since the Handler design is going away soon.)
            mComparatorModel = mModelBuilder.buildFromRankedList((List<AppTarget>) msg.obj);
        } else if (msg.obj == null && mResolverRankerService == null) {
            Log.e(TAG, "Unexpected null result");
        }
    }

    @Override
    void doCompute(List<ResolvedComponentInfo> targets) {
        if (targets.isEmpty()) {
@@ -104,121 +141,113 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
                sortedAppTargets -> {
                    if (sortedAppTargets.isEmpty()) {
                        Log.i(TAG, "AppPredictionService disabled. Using resolver.");
                        // APS for chooser is disabled. Fallback to resolver.
                        mResolverRankerService =
                                new ResolverRankerServiceResolverComparator(
                                        mContext, mIntent, mReferrerPackage,
                                        () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
                                        getChooserActivityLogger());
                        mComparatorModel = buildUpdatedModel();
                        mResolverRankerService.compute(targets);
                        setupFallbackModel(targets);
                    } else {
                        Log.i(TAG, "AppPredictionService response received");
                        // Skip sending to Handler which takes extra time to dispatch messages.
                        // TODO: the Handler guards some concurrency conditions, so this could
                        // probably result in a race (we're not currently on the Handler thread?).
                        // We'll leave this as-is since we intend to remove the Handler design
                        // shortly, but this is still an unsound shortcut.
                        handleResult(sortedAppTargets);
                    }
                }
        );
    }

    @Override
    void handleResultMessage(Message msg) {
        // Null value is okay if we have defaulted to the ResolverRankerService.
        if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
            final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
            handleSortedAppTargets(sortedAppTargets);
        } else if (msg.obj == null && mResolverRankerService == null) {
            Log.e(TAG, "Unexpected null result");
        }
    private void setupFallbackModel(List<ResolvedComponentInfo> targets) {
        mResolverRankerService =
                new ResolverRankerServiceResolverComparator(
                        mContext, mIntent, mReferrerPackage,
                        () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
                        getChooserActivityLogger());
        mComparatorModel = mModelBuilder.buildFallbackModel(mResolverRankerService);
        mResolverRankerService.compute(targets);
    }

    private void handleResult(List<AppTarget> sortedAppTargets) {
        if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) {
            handleSortedAppTargets(sortedAppTargets);
            mComparatorModel = mModelBuilder.buildFromRankedList(sortedAppTargets);
            mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
            afterCompute();
        }
    }

    private void handleSortedAppTargets(List<AppTarget> sortedAppTargets) {
        if (checkAppTargetRankValid(sortedAppTargets)) {
            sortedAppTargets.forEach(target -> mTargetScores.put(
                    new ComponentName(target.getPackageName(), target.getClassName()),
                    target.getRank()));
    static class ModelBuilder {
        private final AppPredictor mAppPredictor;
        private final UserHandle mUser;

        ModelBuilder(AppPredictor appPredictor, UserHandle user) {
            mAppPredictor = appPredictor;
            mUser = user;
        }

        ResolverComparatorModel buildFromRankedList(List<AppTarget> sortedAppTargets) {
            return new AppPredictionServiceComparatorModel(
                    mAppPredictor, mUser, buildTargetRanksMapFromSortedTargets(sortedAppTargets));
        }

        ResolverComparatorModel buildFallbackModel(
                ResolverRankerServiceResolverComparator fallback) {
            return adaptLegacyResolverComparatorToComparatorModel(fallback);
        }

        // The remaining methods would be static if this weren't an inner class (i.e., they don't
        // depend on any ivars, not even the ones in ModelBuilder).

        private Map<ComponentName, Integer> buildTargetRanksMapFromSortedTargets(
                List<AppTarget> sortedAppTargets) {
            Map<ComponentName, Integer> targetRanks = new HashMap<>();
            for (int i = 0; i < sortedAppTargets.size(); i++) {
                ComponentName componentName = new ComponentName(
                        sortedAppTargets.get(i).getPackageName(),
                        sortedAppTargets.get(i).getClassName());
            mTargetRanks.put(componentName, i);
                targetRanks.put(componentName, i);
                Log.i(TAG, "handleSortedAppTargets, sortedAppTargets #" + i + ": " + componentName);
            }
        mComparatorModel = buildUpdatedModel();
    }

    private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) {
        for (AppTarget target : sortedAppTargets) {
            if (target.getRank() != 0) {
                return true;
            }
        }
        return false;
            return targetRanks;
        }

        // TODO: when the refactoring is further along we'll probably have access to the
        // comparator's new ResolverComparatorModel API, so we won't have to adapt from the legacy
        // interface here. On the other hand, AppPredictionServiceResolverComparatorModel (or its
        // replacement counterpart) shouldn't still be responsible for implementing the
        // ResolverRankerService fallback logic at that time.
        private ResolverComparatorModel adaptLegacyResolverComparatorToComparatorModel(
                AbstractResolverComparator comparator) {
            return new ResolverComparatorModel() {
                    @Override
    float getScore(ComponentName name) {
        return mComparatorModel.getScore(name);
                    public Comparator<ResolveInfo> getComparator() {
                        // Adapt the base type, which doesn't declare itself to be an implementation
                        // of {@code Comparator<ResolveInfo>} even though it has the one method.
                        return (lhs, rhs) -> comparator.compare(lhs, rhs);
                    }

                    @Override
    void updateModel(ComponentName componentName) {
        mComparatorModel.notifyOnTargetSelected(componentName);
                    public float getScore(ComponentName componentName) {
                        return comparator.getScore(componentName);
                    }

                    @Override
    void destroy() {
        if (mResolverRankerService != null) {
            mResolverRankerService.destroy();
            mResolverRankerService = null;
            mComparatorModel = buildUpdatedModel();
                    public void notifyOnTargetSelected(ComponentName componentName) {
                        comparator.updateModel(componentName);
                    }
                };
        }

    /**
     * Re-construct an {@code AppPredictionServiceComparatorModel} to replace the current model
     * instance (if any) using the up-to-date {@code AppPredictionServiceResolverComparator} ivar
     * values.
     *
     * TODO: each time we replace the model instance, we're either updating the model to use
     * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars
     * that wasn't available the last time the model was updated. For those latter cases, we should
     * just avoid creating the model altogether until we have all the prerequisites we'll need. Then
     * we can probably simplify the logic in {@code AppPredictionServiceComparatorModel} since we
     * won't need to handle edge cases when the model data isn't fully prepared.
     * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished
     * initializing the first time and now want to adjust some data, but still need to wait for
     * changes to propagate to the other ivars before rebuilding the model.)
     */
    private AppPredictionServiceComparatorModel buildUpdatedModel() {
        return new AppPredictionServiceComparatorModel(
                mAppPredictor, mResolverRankerService, mUser, mTargetRanks);
    }

    // TODO: Finish separating behaviors of AbstractResolverComparator, then (probably) make this a
    // standalone class once clients are written in terms of ResolverComparatorModel.
    static class AppPredictionServiceComparatorModel implements ResolverComparatorModel {
        private final AppPredictor mAppPredictor;
        private final ResolverRankerServiceResolverComparator mResolverRankerService;
        private final UserHandle mUser;
        private final Map<ComponentName, Integer> mTargetRanks;  // Treat as immutable.

        AppPredictionServiceComparatorModel(
                AppPredictor appPredictor,
                @Nullable ResolverRankerServiceResolverComparator resolverRankerService,
                UserHandle user,
                Map<ComponentName, Integer> targetRanks) {
            mAppPredictor = appPredictor;
            mResolverRankerService = resolverRankerService;
            mUser = user;
            mTargetRanks = targetRanks;
        }
@@ -226,9 +255,6 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
        @Override
        public Comparator<ResolveInfo> getComparator() {
            return (lhs, rhs) -> {
                if (mResolverRankerService != null) {
                    return mResolverRankerService.compare(lhs, rhs);
                }
                Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
                        lhs.activityInfo.name));
                Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
@@ -246,9 +272,6 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator

        @Override
        public float getScore(ComponentName name) {
            if (mResolverRankerService != null) {
                return mResolverRankerService.getScore(name);
            }
            Integer rank = mTargetRanks.get(name);
            if (rank == null) {
                Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
@@ -260,10 +283,6 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator

        @Override
        public void notifyOnTargetSelected(ComponentName componentName) {
            if (mResolverRankerService != null) {
                mResolverRankerService.updateModel(componentName);
                return;
            }
            mAppPredictor.notifyAppTargetEvent(
                    new AppTargetEvent.Builder(
                        new AppTarget.Builder(