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

Commit c82e0d4e authored by Danning Chen's avatar Danning Chen Committed by Android (Google) Code Review
Browse files

Merge "Create ShareSheetPredictor in People Service."

parents 5c6d3687 be2c1552
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ import android.os.RemoteException;
import android.util.Slog;

import com.android.server.people.data.DataManager;
import com.android.server.people.prediction.ConversationPredictor;
import com.android.server.people.prediction.AppTargetPredictor;

import java.util.List;

@@ -34,12 +34,12 @@ class SessionInfo {

    private static final String TAG = "SessionInfo";

    private final ConversationPredictor mConversationPredictor;
    private final AppTargetPredictor mAppTargetPredictor;
    private final RemoteCallbackList<IPredictionCallback> mCallbacks =
            new RemoteCallbackList<>();

    SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
        mConversationPredictor = new ConversationPredictor(predictionContext,
        mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
                this::updatePredictions, dataManager);
    }

@@ -51,8 +51,8 @@ class SessionInfo {
        mCallbacks.unregister(callback);
    }

    ConversationPredictor getPredictor() {
        return mConversationPredictor;
    AppTargetPredictor getPredictor() {
        return mAppTargetPredictor;
    }

    void onDestroy() {
+14 −31
Original line number Diff line number Diff line
@@ -59,7 +59,6 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
@@ -199,6 +198,13 @@ public class DataManager {
        }
    }

    /** Gets the {@link PackageData} for the given package and user. */
    @Nullable
    public PackageData getPackage(@NonNull String packageName, @UserIdInt int userId) {
        UserData userData = getUnlockedUserData(userId);
        return userData != null ? userData.getPackageData(packageName) : null;
    }

    /** Gets the {@link ShortcutInfo} for the given shortcut ID. */
    @Nullable
    public ShortcutInfo getShortcut(@NonNull String packageName, @UserIdInt int userId,
@@ -212,20 +218,11 @@ public class DataManager {
    }

    /**
     * Gets the conversation {@link ShareShortcutInfo}s from all packages owned by the calling user
     * that match the specified {@link IntentFilter}.
     * Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
     * the specified {@link IntentFilter}.
     */
    public List<ShareShortcutInfo> getConversationShareTargets(
            @NonNull IntentFilter intentFilter) {
        List<ShareShortcutInfo> shareShortcuts = mShortcutManager.getShareTargets(intentFilter);
        List<ShareShortcutInfo> result = new ArrayList<>();
        for (ShareShortcutInfo shareShortcut : shareShortcuts) {
            ShortcutInfo si = shareShortcut.getShortcutInfo();
            if (getConversationInfo(si.getPackage(), si.getUserId(), si.getId()) != null) {
                result.add(shareShortcut);
            }
        }
        return result;
    public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
        return mShortcutManager.getShareTargets(intentFilter);
    }

    /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -236,7 +233,7 @@ public class DataManager {
        if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
            return;
        }
        PackageData packageData = getPackageData(appTarget.getPackageName(),
        PackageData packageData = getPackage(appTarget.getPackageName(),
                appTarget.getUser().getIdentifier());
        if (packageData == null) {
            return;
@@ -283,20 +280,6 @@ public class DataManager {
        return userData != null && userData.isUnlocked() ? userData : null;
    }

    @Nullable
    private PackageData getPackageData(@NonNull String packageName, int userId) {
        UserData userData = getUnlockedUserData(userId);
        return userData != null ? userData.getPackageData(packageName) : null;
    }

    @Nullable
    private ConversationInfo getConversationInfo(@NonNull String packageName, @UserIdInt int userId,
            @NonNull String shortcutId) {
        PackageData packageData = getPackageData(packageName, userId);
        return packageData != null ? packageData.getConversationStore().getConversation(shortcutId)
                : null;
    }

    private void updateDefaultDialer(@NonNull UserData userData) {
        TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
        String defaultDialer = telecomManager != null
@@ -318,7 +301,7 @@ public class DataManager {
        if (shortcutId == null) {
            return null;
        }
        PackageData packageData = getPackageData(sbn.getPackageName(),
        PackageData packageData = getPackage(sbn.getPackageName(),
                sbn.getUser().getIdentifier());
        if (packageData == null
                || packageData.getConversationStore().getConversation(shortcutId) == null) {
@@ -382,7 +365,7 @@ public class DataManager {
            usageEvents.getNextEvent(e);

            String packageName = e.getPackageName();
            PackageData packageData = getPackageData(packageName, userId);
            PackageData packageData = getPackage(packageName, userId);
            if (packageData == null) {
                continue;
            }
+10 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.people.data;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.LocusId;
import android.text.TextUtils;
@@ -67,6 +68,15 @@ public class PackageData {
        return getEventStore().getPackageEventHistory();
    }

    /**
     * Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
     * ConversationInfo} does not exist.
     */
    @Nullable
    public ConversationInfo getConversationInfo(@NonNull String shortcutId) {
        return getConversationStore().getConversation(shortcutId);
    }

    /**
     * Gets the combined {@link EventHistory} for a given shortcut ID. This returned {@link
     * EventHistory} has events of all types, no matter whether they're annotated with shortcut ID,
+128 −0
Original line number Diff line number Diff line
@@ -18,53 +18,50 @@ package com.android.server.people.prediction;

import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

/**
 * Predictor that predicts the conversations or apps the user is most likely to open.
 * Predictor that predicts the {@link AppTarget} the user is most likely to open.
 */
public class ConversationPredictor {
public class AppTargetPredictor {

    private static final String UI_SURFACE_SHARE = "share";

    /** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
    public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
            @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
            @NonNull DataManager dataManager) {
        if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
            return new ShareTargetPredictor(
                    predictionContext, updatePredictionsMethod, dataManager);
        }
        return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
    }

    private final AppPredictionContext mPredictionContext;
    private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
    private final DataManager mDataManager;
    private final ExecutorService mCallbackExecutor;
    @Nullable
    private final IntentFilter mIntentFilter;

    public ConversationPredictor(@NonNull AppPredictionContext predictionContext,
    AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
            @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
            @NonNull DataManager dataManager) {
        mPredictionContext = predictionContext;
        mUpdatePredictionsMethod = updatePredictionsMethod;
        mDataManager = dataManager;
        mCallbackExecutor = Executors.newSingleThreadExecutor();
        if (UI_SURFACE_SHARE.equals(mPredictionContext.getUiSurface())) {
            mIntentFilter = mPredictionContext.getExtras().getParcelable(
                    ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
        } else {
            mIntentFilter = null;
        }
    }

    /**
@@ -72,14 +69,14 @@ public class ConversationPredictor {
     */
    @MainThread
    public void onAppTargetEvent(AppTargetEvent event) {
        mDataManager.reportAppTargetEvent(event, mIntentFilter);
    }

    /**
     * Called by the client app to indicate a particular location has been shown to the user.
     */
    @MainThread
    public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {}
    public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
    }

    /**
     * Called by the client app to request sorting of the provided targets based on the prediction
@@ -87,7 +84,7 @@ public class ConversationPredictor {
     */
    @MainThread
    public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
        mCallbackExecutor.execute(() -> callback.accept(targets));
        mCallbackExecutor.execute(() -> sortTargets(targets, callback));
    }

    /**
@@ -95,48 +92,37 @@ public class ConversationPredictor {
     */
    @MainThread
    public void onRequestPredictionUpdate() {
        // TODO: Re-route the call to different ranking classes for different surfaces.
        mCallbackExecutor.execute(() -> {
            List<AppTarget> targets = new ArrayList<>();
            if (mIntentFilter != null) {
                List<ShareShortcutInfo> shareShortcuts =
                        mDataManager.getConversationShareTargets(mIntentFilter);
                for (ShareShortcutInfo shareShortcut : shareShortcuts) {
                    ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
                    AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
                    String shareTargetClass = shareShortcut.getTargetComponent().getClassName();
                    targets.add(new AppTarget.Builder(appTargetId, shortcutInfo)
                            .setClassName(shareTargetClass)
                            .build());
        mCallbackExecutor.execute(this::predictTargets);
    }

    @VisibleForTesting
    public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
        return mUpdatePredictionsMethod;
    }
            } else {
                List<ConversationData> conversationDataList = new ArrayList<>();
                mDataManager.forAllPackages(packageData ->
                        packageData.forAllConversations(conversationInfo -> {
                            EventHistory eventHistory = packageData.getEventHistory(
                                    conversationInfo.getShortcutId());
                            ConversationData conversationData = new ConversationData(
                                    packageData.getPackageName(), packageData.getUserId(),
                                    conversationInfo, eventHistory);
                            conversationDataList.add(conversationData);
                        }));
                for (ConversationData conversationData : conversationDataList) {
                    String shortcutId = conversationData.getConversationInfo().getShortcutId();
                    ShortcutInfo shortcut = mDataManager.getShortcut(
                            conversationData.getPackageName(), conversationData.getUserId(),
                            shortcutId);
                    if (shortcut != null) {
                        AppTargetId appTargetId = new AppTargetId(shortcut.getId());
                        targets.add(new AppTarget.Builder(appTargetId, shortcut).build());

    /** To be overridden by the subclass to predict the targets. */
    @WorkerThread
    void predictTargets() {
    }

    /**
     * To be overridden by the subclass to sort the provided targets based on the prediction
     * ranking.
     */
    @WorkerThread
    void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
        callback.accept(targets);
    }

    AppPredictionContext getPredictionContext() {
        return mPredictionContext;
    }
            mUpdatePredictionsMethod.accept(targets);
        });

    DataManager getDataManager() {
        return mDataManager;
    }

    @VisibleForTesting
    public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
        return mUpdatePredictionsMethod;
    void updatePredictions(List<AppTarget> targets) {
        mUpdatePredictionsMethod.accept(targets);
    }
}
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.server.people.prediction;

import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;
import com.android.server.people.data.PackageData;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

class ShareTargetPredictor extends AppTargetPredictor {

    private final IntentFilter mIntentFilter;

    ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
            @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
            @NonNull DataManager dataManager) {
        super(predictionContext, updatePredictionsMethod, dataManager);
        mIntentFilter = predictionContext.getExtras().getParcelable(
                ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
    }

    @MainThread
    @Override
    public void onAppTargetEvent(AppTargetEvent event) {
        getDataManager().reportAppTargetEvent(event, mIntentFilter);
    }

    @WorkerThread
    @Override
    protected void predictTargets() {
        List<ShareTarget> shareTargets = getShareTargets();
        // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
        List<AppTarget> appTargets = new ArrayList<>();
        for (ShareTarget shareTarget : shareTargets) {

            ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
            AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
            String shareTargetClassName =
                    shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
            AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
                    .setClassName(shareTargetClassName)
                    .build();
            appTargets.add(appTarget);
            if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
                break;
            }
        }
        updatePredictions(appTargets);
    }

    @VisibleForTesting
    List<ShareTarget> getShareTargets() {
        List<ShareTarget> shareTargets = new ArrayList<>();
        List<ShareShortcutInfo> shareShortcuts =
                getDataManager().getShareShortcuts(mIntentFilter);

        for (ShareShortcutInfo shareShortcut : shareShortcuts) {
            ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
            String packageName = shortcutInfo.getPackage();
            int userId = shortcutInfo.getUserId();
            PackageData packageData = getDataManager().getPackage(packageName, userId);

            ConversationData conversationData = null;
            if (packageData != null) {
                String shortcutId = shortcutInfo.getId();
                ConversationInfo conversationInfo =
                        packageData.getConversationInfo(shortcutId);

                if (conversationInfo != null) {
                    EventHistory eventHistory = packageData.getEventHistory(shortcutId);
                    conversationData = new ConversationData(
                            packageName, userId, conversationInfo, eventHistory);
                }
            }
            shareTargets.add(new ShareTarget(shareShortcut, conversationData));
        }

        return shareTargets;
    }

    @VisibleForTesting
    static class ShareTarget {

        @NonNull
        private final ShareShortcutInfo mShareShortcutInfo;
        @Nullable
        private final ConversationData mConversationData;

        private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
                @Nullable ConversationData conversationData) {
            mShareShortcutInfo = shareShortcutInfo;
            mConversationData = conversationData;
        }

        @NonNull
        @VisibleForTesting
        ShareShortcutInfo getShareShortcutInfo() {
            return mShareShortcutInfo;
        }

        @Nullable
        @VisibleForTesting
        ConversationData getConversationData() {
            return mConversationData;
        }
    }
}
Loading