• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.pm;
17 
18 import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
19 import static com.android.launcher3.pm.PackageInstallInfo.STATUS_FAILED;
20 import static com.android.launcher3.pm.PackageInstallInfo.STATUS_INSTALLED;
21 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
22 
23 import android.content.pm.LauncherApps;
24 import android.content.pm.PackageInstaller;
25 import android.content.pm.PackageInstaller.SessionInfo;
26 import android.os.Build;
27 import android.os.UserHandle;
28 import android.util.SparseArray;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.WorkerThread;
33 
34 import com.android.launcher3.Flags;
35 import com.android.launcher3.Utilities;
36 import com.android.launcher3.util.PackageUserKey;
37 
38 import java.lang.ref.WeakReference;
39 import java.util.Objects;
40 
41 @SuppressWarnings("NewApi")
42 @WorkerThread
43 public class InstallSessionTracker extends PackageInstaller.SessionCallback {
44 
45     // Lazily initialized
46     private SparseArray<PackageUserKey> mActiveSessions = null;
47 
48     @NonNull
49     private final WeakReference<InstallSessionHelper> mWeakHelper;
50 
51     @NonNull
52     private final WeakReference<Callback> mWeakCallback;
53 
54     @NonNull
55     private final PackageInstaller mInstaller;
56 
57     @Nullable
58     private final LauncherApps mLauncherApps;
59 
60 
InstallSessionTracker(@ullable final InstallSessionHelper installerCompat, @Nullable final Callback callback, @NonNull final PackageInstaller installer, @Nullable LauncherApps launcherApps)61     InstallSessionTracker(@Nullable final InstallSessionHelper installerCompat,
62             @Nullable final Callback callback, @NonNull final PackageInstaller installer,
63             @Nullable LauncherApps launcherApps) {
64         mWeakHelper = new WeakReference<>(installerCompat);
65         mWeakCallback = new WeakReference<>(callback);
66         mInstaller = installer;
67         mLauncherApps = launcherApps;
68     }
69 
70     @Override
onCreated(final int sessionId)71     public void onCreated(final int sessionId) {
72         InstallSessionHelper helper = mWeakHelper.get();
73         Callback callback = mWeakCallback.get();
74         if (callback == null || helper == null) {
75             return;
76         }
77         SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
78         if (sessionInfo != null) {
79             callback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
80         }
81 
82         helper.tryQueuePromiseAppIcon(sessionInfo);
83 
84         if (Flags.enableSupportForArchiving() && sessionInfo != null
85                 && sessionInfo.isUnarchival()) {
86             // For archived apps, icon could already be present on the workspace. To make sure
87             // the icon state is updated, we send a change event.
88             callback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(sessionInfo));
89         }
90     }
91 
92     @Override
onFinished(final int sessionId, final boolean success)93     public void onFinished(final int sessionId, final boolean success) {
94         InstallSessionHelper helper = mWeakHelper.get();
95         Callback callback = mWeakCallback.get();
96         if (callback == null || helper == null) {
97             return;
98         }
99         // For a finished session, we can't get the session info. So use the
100         // packageName from our local cache.
101         SparseArray<PackageUserKey> activeSessions = getActiveSessionMap(helper);
102         PackageUserKey key = activeSessions.get(sessionId);
103         activeSessions.remove(sessionId);
104 
105         if (key != null && key.mPackageName != null) {
106             String packageName = key.mPackageName;
107             PackageInstallInfo info = PackageInstallInfo.fromState(
108                     success ? STATUS_INSTALLED : STATUS_FAILED,
109                     packageName, key.mUser);
110             callback.onPackageStateChanged(info);
111 
112             if (!success && helper.promiseIconAddedForId(sessionId)) {
113                 callback.onSessionFailure(packageName, key.mUser);
114                 // If it is successful, the id is removed in the the package added flow.
115                 helper.removePromiseIconId(sessionId);
116             }
117         }
118     }
119 
120     @Override
onProgressChanged(final int sessionId, final float progress)121     public void onProgressChanged(final int sessionId, final float progress) {
122         InstallSessionHelper helper = mWeakHelper.get();
123         Callback callback = mWeakCallback.get();
124         if (callback == null || helper == null) {
125             return;
126         }
127         SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
128         if (session != null && session.getAppPackageName() != null) {
129             callback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(session));
130         }
131     }
132 
133     @Override
onActiveChanged(final int sessionId, final boolean active)134     public void onActiveChanged(final int sessionId, final boolean active) { }
135 
136     @Override
onBadgingChanged(final int sessionId)137     public void onBadgingChanged(final int sessionId) {
138         InstallSessionHelper helper = mWeakHelper.get();
139         Callback callback = mWeakCallback.get();
140         if (callback == null || helper == null) {
141             return;
142         }
143         SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
144         if (sessionInfo != null) {
145             helper.tryQueuePromiseAppIcon(sessionInfo);
146         }
147     }
148 
149     @Nullable
pushSessionDisplayToLauncher(final int sessionId, @NonNull final InstallSessionHelper helper, @NonNull final Callback callback)150     private SessionInfo pushSessionDisplayToLauncher(final int sessionId,
151             @NonNull final InstallSessionHelper helper, @NonNull final Callback callback) {
152         SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
153         if (session != null && session.getAppPackageName() != null) {
154             PackageUserKey key =
155                     new PackageUserKey(session.getAppPackageName(), getUserHandle(session));
156             getActiveSessionMap(helper).put(session.getSessionId(), key);
157             callback.onUpdateSessionDisplay(key, session);
158             return session;
159         }
160         return null;
161     }
162 
163     @NonNull
getActiveSessionMap( @onNull final InstallSessionHelper helper)164     private SparseArray<PackageUserKey> getActiveSessionMap(
165             @NonNull final InstallSessionHelper helper) {
166         if (mActiveSessions == null) {
167             mActiveSessions = new SparseArray<>();
168             helper.getActiveSessions().forEach(
169                     (key, si) -> mActiveSessions.put(si.getSessionId(), key));
170         }
171         return mActiveSessions;
172     }
173 
register()174     void register() {
175         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
176             mInstaller.registerSessionCallback(this, MODEL_EXECUTOR.getHandler());
177         } else {
178             Objects.requireNonNull(mLauncherApps).registerPackageInstallerSessionCallback(
179                     MODEL_EXECUTOR, this);
180         }
181     }
182 
unregister()183     public void unregister() {
184         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
185             mInstaller.unregisterSessionCallback(this);
186         } else {
187             Objects.requireNonNull(mLauncherApps).unregisterPackageInstallerSessionCallback(this);
188         }
189     }
190 
191     public interface Callback {
192 
onSessionFailure(@onNull String packageName, @NonNull UserHandle user)193         void onSessionFailure(@NonNull String packageName, @NonNull UserHandle user);
194 
onUpdateSessionDisplay(@onNull PackageUserKey key, @NonNull SessionInfo info)195         void onUpdateSessionDisplay(@NonNull PackageUserKey key, @NonNull SessionInfo info);
196 
onPackageStateChanged(@onNull PackageInstallInfo info)197         void onPackageStateChanged(@NonNull PackageInstallInfo info);
198 
onInstallSessionCreated(@onNull PackageInstallInfo info)199         void onInstallSessionCreated(@NonNull PackageInstallInfo info);
200     }
201 }
202