• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
17 package com.android.launcher3.pm;
18 
19 import android.content.Context;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.LauncherApps;
22 import android.content.pm.PackageInstaller;
23 import android.content.pm.PackageInstaller.SessionInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Process;
26 import android.os.UserHandle;
27 import android.text.TextUtils;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 import androidx.annotation.WorkerThread;
32 
33 import com.android.launcher3.LauncherPrefs;
34 import com.android.launcher3.SessionCommitReceiver;
35 import com.android.launcher3.Utilities;
36 import com.android.launcher3.logging.FileLog;
37 import com.android.launcher3.model.ItemInstallQueue;
38 import com.android.launcher3.util.IntArray;
39 import com.android.launcher3.util.IntSet;
40 import com.android.launcher3.util.MainThreadInitializedObject;
41 import com.android.launcher3.util.PackageManagerHelper;
42 import com.android.launcher3.util.PackageUserKey;
43 import com.android.launcher3.util.Preconditions;
44 
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Objects;
50 
51 /**
52  * Utility class to tracking install sessions
53  */
54 public class InstallSessionHelper {
55 
56     @NonNull
57     private static final String LOG = "InstallSessionHelper";
58 
59     // Set<String> of session ids of promise icons that have been added to the home screen
60     // as FLAG_PROMISE_NEW_INSTALLS.
61     @NonNull
62     public static final String PROMISE_ICON_IDS = "promise_icon_ids";
63 
64     private static final boolean DEBUG = false;
65 
66     @NonNull
67     public static final MainThreadInitializedObject<InstallSessionHelper> INSTANCE =
68             new MainThreadInitializedObject<>(InstallSessionHelper::new);
69 
70     @Nullable
71     private final LauncherApps mLauncherApps;
72 
73     @NonNull
74     private final Context mAppContext;
75 
76     @NonNull
77     private final PackageInstaller mInstaller;
78 
79     @NonNull
80     private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
81 
82     @Nullable
83     private IntSet mPromiseIconIds;
84 
InstallSessionHelper(@onNull final Context context)85     public InstallSessionHelper(@NonNull final Context context) {
86         mInstaller = context.getPackageManager().getPackageInstaller();
87         mAppContext = context.getApplicationContext();
88         mLauncherApps = context.getSystemService(LauncherApps.class);
89     }
90 
91     @WorkerThread
92     @NonNull
getPromiseIconIds()93     private IntSet getPromiseIconIds() {
94         Preconditions.assertWorkerThread();
95         if (mPromiseIconIds != null) {
96             return mPromiseIconIds;
97         }
98         mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
99                 LauncherPrefs.get(mAppContext).get(LauncherPrefs.PROMISE_ICON_IDS)));
100 
101         IntArray existingIds = new IntArray();
102         for (SessionInfo info : getActiveSessions().values()) {
103             existingIds.add(info.getSessionId());
104         }
105         IntArray idsToRemove = new IntArray();
106 
107         for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
108             if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
109                 idsToRemove.add(mPromiseIconIds.getArray().get(i));
110             }
111         }
112         for (int i = idsToRemove.size() - 1; i >= 0; --i) {
113             mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
114         }
115         return mPromiseIconIds;
116     }
117 
118     @NonNull
getActiveSessions()119     public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
120         HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
121         for (SessionInfo info : getAllVerifiedSessions()) {
122             activePackages.put(new PackageUserKey(info.getAppPackageName(), getUserHandle(info)),
123                     info);
124         }
125         return activePackages;
126     }
127 
128     @Nullable
getActiveSessionInfo(UserHandle user, String pkg)129     public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
130         for (SessionInfo info : getAllVerifiedSessions()) {
131             boolean match = pkg.equals(info.getAppPackageName());
132             if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
133                 match = false;
134             }
135             if (match) {
136                 return info;
137             }
138         }
139         return null;
140     }
141 
updatePromiseIconPrefs()142     private void updatePromiseIconPrefs() {
143         LauncherPrefs.get(mAppContext).put(LauncherPrefs.PROMISE_ICON_IDS,
144                 getPromiseIconIds().getArray().toConcatString());
145     }
146 
147     @Nullable
getVerifiedSessionInfo(final int sessionId)148     SessionInfo getVerifiedSessionInfo(final int sessionId) {
149         return verify(mInstaller.getSessionInfo(sessionId));
150     }
151 
152     @Nullable
verify(@ullable final SessionInfo sessionInfo)153     private SessionInfo verify(@Nullable final SessionInfo sessionInfo) {
154         if (sessionInfo == null
155                 || sessionInfo.getInstallerPackageName() == null
156                 || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
157             return null;
158         }
159         return isTrustedPackage(sessionInfo.getInstallerPackageName(), getUserHandle(sessionInfo))
160                 ? sessionInfo : null;
161     }
162 
163     /**
164      * Returns true if the provided packageName can be trusted for user configurations
165      */
isTrustedPackage(String pkg, UserHandle user)166     public boolean isTrustedPackage(String pkg, UserHandle user) {
167         synchronized (mSessionVerifiedMap) {
168             if (!mSessionVerifiedMap.containsKey(pkg)) {
169                 boolean hasSystemFlag = DEBUG || mAppContext.getPackageName().equals(pkg)
170                         || new PackageManagerHelper(mAppContext)
171                                 .getApplicationInfo(pkg, user, ApplicationInfo.FLAG_SYSTEM) != null;
172                 mSessionVerifiedMap.put(pkg, hasSystemFlag);
173             }
174         }
175         return mSessionVerifiedMap.get(pkg);
176     }
177 
178     @NonNull
getAllVerifiedSessions()179     public List<SessionInfo> getAllVerifiedSessions() {
180         List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
181                 ? Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions()
182                 : mInstaller.getAllSessions());
183         Iterator<SessionInfo> it = list.iterator();
184         while (it.hasNext()) {
185             if (verify(it.next()) == null) {
186                 it.remove();
187             }
188         }
189         return list;
190     }
191 
192     @WorkerThread
promiseIconAddedForId(final int sessionId)193     public boolean promiseIconAddedForId(final int sessionId) {
194         return getPromiseIconIds().contains(sessionId);
195     }
196 
197     @WorkerThread
removePromiseIconId(final int sessionId)198     public void removePromiseIconId(final int sessionId) {
199         if (promiseIconAddedForId(sessionId)) {
200             getPromiseIconIds().getArray().removeValue(sessionId);
201             updatePromiseIconPrefs();
202         }
203     }
204 
205     /**
206      * Add a promise app icon to the workspace iff:
207      * - The settings for it are enabled
208      * - The user installed the app
209      * - There is an app icon and label (For apps with no launching activity, no icon is provided).
210      * - The app is not already installed
211      * - A promise icon for the session has not already been created
212      */
213     @WorkerThread
tryQueuePromiseAppIcon(@ullable final PackageInstaller.SessionInfo sessionInfo)214     void tryQueuePromiseAppIcon(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
215         if (SessionCommitReceiver.isEnabled(mAppContext)
216                 && verifySessionInfo(sessionInfo)
217                 && !promiseIconAddedForId(sessionInfo.getSessionId())) {
218             FileLog.d(LOG, "Adding package name to install queue: "
219                     + sessionInfo.getAppPackageName());
220 
221             ItemInstallQueue.INSTANCE.get(mAppContext)
222                     .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
223 
224             getPromiseIconIds().add(sessionInfo.getSessionId());
225             updatePromiseIconPrefs();
226         }
227     }
228 
verifySessionInfo(@ullable final PackageInstaller.SessionInfo sessionInfo)229     public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
230         return verify(sessionInfo) != null
231                 && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
232                 && sessionInfo.getAppIcon() != null
233                 && !TextUtils.isEmpty(sessionInfo.getAppLabel())
234                 && !new PackageManagerHelper(mAppContext).isAppInstalled(
235                         sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
236     }
237 
registerInstallTracker( @ullable final InstallSessionTracker.Callback callback)238     public InstallSessionTracker registerInstallTracker(
239             @Nullable final InstallSessionTracker.Callback callback) {
240         InstallSessionTracker tracker = new InstallSessionTracker(
241                 this, callback, mInstaller, mLauncherApps);
242         tracker.register();
243         return tracker;
244     }
245 
getUserHandle(@onNull final SessionInfo info)246     public static UserHandle getUserHandle(@NonNull final SessionInfo info) {
247         return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
248     }
249 }
250