1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.model;
17 
18 import static android.os.Process.myUserHandle;
19 
20 import android.app.PendingIntent;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageInstaller.SessionInfo;
24 import android.util.Log;
25 
26 import com.android.launcher3.LauncherSettings;
27 import com.android.launcher3.model.data.FolderInfo;
28 import com.android.launcher3.model.data.ItemInfo;
29 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
30 import com.android.launcher3.util.MultiHashMap;
31 import com.android.launcher3.util.PackageUserKey;
32 
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 
40 /**
41  * Helper class to send broadcasts to package installers that have:
42  * - Items on the first screen
43  * - Items with an active install session
44  *
45  * The packages are broken down by: folder items, workspace items, hotseat items, and widgets.
46  *
47  * Package installers only receive data for items that they are installing.
48  */
49 public class FirstScreenBroadcast {
50 
51     private static final String TAG = "FirstScreenBroadcast";
52     private static final boolean DEBUG = false;
53 
54     private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
55             = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
56 
57     private static final String FOLDER_ITEM_EXTRA = "folderItem";
58     private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
59     private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
60     private static final String WIDGET_ITEM_EXTRA = "widgetItem";
61 
62     private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
63 
64     private final MultiHashMap<String, String> mPackagesForInstaller;
65 
FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage)66     public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
67         mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
68     }
69 
70     /**
71      * @return Map where the key is the package name of the installer, and the value is a list
72      *         of packages with active sessions for that installer.
73      */
getPackagesForInstaller( HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage)74     private MultiHashMap<String, String> getPackagesForInstaller(
75             HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
76         MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
77         for (Map.Entry<PackageUserKey, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
78             if (myUserHandle().equals(entry.getKey().mUser)) {
79                 packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
80                         entry.getKey().mPackageName);
81             }
82         }
83         return packagesForInstaller;
84     }
85 
86     /**
87      * Sends a broadcast to all package installers that have items with active sessions on the users
88      * first screen.
89      */
sendBroadcasts(Context context, List<ItemInfo> firstScreenItems)90     public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
91         for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
92             sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
93         }
94     }
95 
96     /**
97      * @param installerPackageName Package name of the package installer.
98      * @param packages List of packages with active sessions for this package installer.
99      * @param firstScreenItems List of items on the first screen.
100      */
sendBroadcastToInstaller(Context context, String installerPackageName, List<String> packages, List<ItemInfo> firstScreenItems)101     private void sendBroadcastToInstaller(Context context, String installerPackageName,
102             List<String> packages, List<ItemInfo> firstScreenItems) {
103         Set<String> folderItems = new HashSet<>();
104         Set<String> workspaceItems = new HashSet<>();
105         Set<String> hotseatItems = new HashSet<>();
106         Set<String> widgetItems = new HashSet<>();
107 
108         for (ItemInfo info : firstScreenItems) {
109             if (info instanceof FolderInfo) {
110                 FolderInfo folderInfo = (FolderInfo) info;
111                 String folderItemInfoPackage;
112                 for (ItemInfo folderItemInfo : folderInfo.contents) {
113                     folderItemInfoPackage = getPackageName(folderItemInfo);
114                     if (folderItemInfoPackage != null
115                             && packages.contains(folderItemInfoPackage)) {
116                         folderItems.add(folderItemInfoPackage);
117                     }
118                 }
119             }
120 
121             String packageName = getPackageName(info);
122             if (packageName == null || !packages.contains(packageName)) {
123                 continue;
124             }
125             if (info instanceof LauncherAppWidgetInfo) {
126                 widgetItems.add(packageName);
127             } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
128                 hotseatItems.add(packageName);
129             } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
130                 workspaceItems.add(packageName);
131             }
132         }
133 
134         if (DEBUG) {
135             printList(installerPackageName, "Folder item", folderItems);
136             printList(installerPackageName, "Workspace item", workspaceItems);
137             printList(installerPackageName, "Hotseat item", hotseatItems);
138             printList(installerPackageName, "Widget item", widgetItems);
139         }
140 
141         context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
142                 .setPackage(installerPackageName)
143                 .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
144                 .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
145                 .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
146                 .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
147                 .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
148                         new Intent(), PendingIntent.FLAG_ONE_SHOT)));
149     }
150 
getPackageName(ItemInfo info)151     private static String getPackageName(ItemInfo info) {
152         String packageName = null;
153         if (info instanceof LauncherAppWidgetInfo) {
154             LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
155             if (widgetInfo.providerName != null) {
156                 packageName = widgetInfo.providerName.getPackageName();
157             }
158         } else if (info.getTargetComponent() != null){
159             packageName = info.getTargetComponent().getPackageName();
160         }
161         return packageName;
162     }
163 
printList(String packageInstaller, String label, Set<String> packages)164     private static void printList(String packageInstaller, String label, Set<String> packages) {
165         for (String pkg : packages) {
166             Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
167         }
168     }
169 }
170