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