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.WorkerThread; 31 32 import com.android.launcher3.util.PackageUserKey; 33 34 import java.lang.ref.WeakReference; 35 36 @WorkerThread 37 public class InstallSessionTracker extends PackageInstaller.SessionCallback { 38 39 // Lazily initialized 40 private SparseArray<PackageUserKey> mActiveSessions = null; 41 42 private final WeakReference<InstallSessionHelper> mWeakHelper; 43 private final WeakReference<Callback> mWeakCallback; 44 private final PackageInstaller mInstaller; 45 private final LauncherApps mLauncherApps; 46 47 InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback, PackageInstaller installer, LauncherApps launcherApps)48 InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback, 49 PackageInstaller installer, LauncherApps launcherApps) { 50 mWeakHelper = new WeakReference<>(installerCompat); 51 mWeakCallback = new WeakReference<>(callback); 52 mInstaller = installer; 53 mLauncherApps = launcherApps; 54 } 55 56 @Override onCreated(int sessionId)57 public void onCreated(int sessionId) { 58 InstallSessionHelper helper = mWeakHelper.get(); 59 Callback callback = mWeakCallback.get(); 60 if (callback == null || helper == null) { 61 return; 62 } 63 SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback); 64 if (sessionInfo != null) { 65 callback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo)); 66 } 67 68 helper.tryQueuePromiseAppIcon(sessionInfo); 69 } 70 71 @Override onFinished(int sessionId, boolean success)72 public void onFinished(int sessionId, boolean success) { 73 InstallSessionHelper helper = mWeakHelper.get(); 74 Callback callback = mWeakCallback.get(); 75 if (callback == null || helper == null) { 76 return; 77 } 78 // For a finished session, we can't get the session info. So use the 79 // packageName from our local cache. 80 SparseArray<PackageUserKey> activeSessions = getActiveSessionMap(helper); 81 PackageUserKey key = activeSessions.get(sessionId); 82 activeSessions.remove(sessionId); 83 84 if (key != null && key.mPackageName != null) { 85 String packageName = key.mPackageName; 86 PackageInstallInfo info = PackageInstallInfo.fromState( 87 success ? STATUS_INSTALLED : STATUS_FAILED, 88 packageName, key.mUser); 89 callback.onPackageStateChanged(info); 90 91 if (!success && helper.promiseIconAddedForId(sessionId)) { 92 callback.onSessionFailure(packageName, key.mUser); 93 // If it is successful, the id is removed in the the package added flow. 94 helper.removePromiseIconId(sessionId); 95 } 96 } 97 } 98 99 @Override onProgressChanged(int sessionId, float progress)100 public void onProgressChanged(int sessionId, float progress) { 101 InstallSessionHelper helper = mWeakHelper.get(); 102 Callback callback = mWeakCallback.get(); 103 if (callback == null || helper == null) { 104 return; 105 } 106 SessionInfo session = helper.getVerifiedSessionInfo(sessionId); 107 if (session != null && session.getAppPackageName() != null) { 108 callback.onPackageStateChanged(PackageInstallInfo.fromInstallingState(session)); 109 } 110 } 111 112 @Override onActiveChanged(int sessionId, boolean active)113 public void onActiveChanged(int sessionId, boolean active) { } 114 115 @Override onBadgingChanged(int sessionId)116 public void onBadgingChanged(int sessionId) { 117 InstallSessionHelper helper = mWeakHelper.get(); 118 Callback callback = mWeakCallback.get(); 119 if (callback == null || helper == null) { 120 return; 121 } 122 SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback); 123 if (sessionInfo != null) { 124 helper.tryQueuePromiseAppIcon(sessionInfo); 125 } 126 } 127 pushSessionDisplayToLauncher( int sessionId, InstallSessionHelper helper, Callback callback)128 private SessionInfo pushSessionDisplayToLauncher( 129 int sessionId, InstallSessionHelper helper, Callback callback) { 130 SessionInfo session = helper.getVerifiedSessionInfo(sessionId); 131 if (session != null && session.getAppPackageName() != null) { 132 PackageUserKey key = 133 new PackageUserKey(session.getAppPackageName(), getUserHandle(session)); 134 getActiveSessionMap(helper).put(session.getSessionId(), key); 135 callback.onUpdateSessionDisplay(key, session); 136 return session; 137 } 138 return null; 139 } 140 getActiveSessionMap(InstallSessionHelper helper)141 private SparseArray<PackageUserKey> getActiveSessionMap(InstallSessionHelper helper) { 142 if (mActiveSessions == null) { 143 mActiveSessions = new SparseArray<>(); 144 helper.getActiveSessions().forEach( 145 (key, si) -> mActiveSessions.put(si.getSessionId(), key)); 146 } 147 return mActiveSessions; 148 } 149 register()150 void register() { 151 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { 152 mInstaller.registerSessionCallback(this, MODEL_EXECUTOR.getHandler()); 153 } else { 154 mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, this); 155 } 156 } 157 unregister()158 public void unregister() { 159 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { 160 mInstaller.unregisterSessionCallback(this); 161 } else { 162 mLauncherApps.unregisterPackageInstallerSessionCallback(this); 163 } 164 } 165 166 public interface Callback { 167 onSessionFailure(String packageName, UserHandle user)168 void onSessionFailure(String packageName, UserHandle user); 169 onUpdateSessionDisplay(PackageUserKey key, SessionInfo info)170 void onUpdateSessionDisplay(PackageUserKey key, SessionInfo info); 171 onPackageStateChanged(PackageInstallInfo info)172 void onPackageStateChanged(PackageInstallInfo info); 173 onInstallSessionCreated(PackageInstallInfo info)174 void onInstallSessionCreated(PackageInstallInfo info); 175 } 176 } 177