PackageInstallerService.java revision 16c8e3f49497b6046972ae650772f65768366be8
13a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey/*
23a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Copyright (C) 2014 The Android Open Source Project
33a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
43a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
53a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * you may not use this file except in compliance with the License.
63a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * You may obtain a copy of the License at
73a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
83a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
93a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
123a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * See the License for the specific language governing permissions and
143a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * limitations under the License.
153a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey */
163a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeypackage com.android.server.pm;
183a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
193a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport static android.content.pm.PackageManager.INSTALL_ALL_USERS;
203a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport static android.content.pm.PackageManager.INSTALL_FROM_ADB;
213a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
223a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
233a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.app.AppOpsManager;
243a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.content.Context;
253a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.content.pm.IPackageDeleteObserver;
263a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.content.pm.IPackageInstaller;
2716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.content.pm.IPackageInstallerCallback;
283a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.content.pm.IPackageInstallerSession;
29bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport android.content.pm.InstallSessionInfo;
30bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport android.content.pm.InstallSessionParams;
3116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.content.pm.PackageManager;
323a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.Binder;
333a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.FileUtils;
343a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.HandlerThread;
353a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.Process;
36a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport android.os.RemoteCallbackList;
37a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport android.os.RemoteException;
38ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport android.os.SELinux;
393a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.UserHandle;
403a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.UserManager;
41ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport android.system.ErrnoException;
42ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport android.system.Os;
433a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.util.ArraySet;
44a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport android.util.ExceptionUtils;
453a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.util.Slog;
463a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.util.SparseArray;
473a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
483a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport com.android.internal.annotations.GuardedBy;
49a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
503a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport com.android.server.IoThread;
513a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport com.google.android.collect.Sets;
523a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
533a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport java.io.File;
54ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport java.io.FilenameFilter;
55ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport java.io.IOException;
56bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport java.util.ArrayList;
57bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport java.util.List;
5816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport java.util.Objects;
593a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
603a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeypublic class PackageInstallerService extends IPackageInstaller.Stub {
613a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private static final String TAG = "PackageInstaller";
623a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
633a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    // TODO: destroy sessions with old timestamps
643a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    // TODO: remove outstanding sessions when installer package goes away
656c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    // TODO: notify listeners in other users when package has been installed there
663a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
673a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final Context mContext;
683a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final PackageManagerService mPm;
693a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final AppOpsManager mAppOps;
703a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
713a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final File mStagingDir;
72ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    private final HandlerThread mInstallThread;
733a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
743a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final Callback mCallback = new Callback();
753a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
763a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @GuardedBy("mSessions")
773a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private int mNextSessionId;
783a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @GuardedBy("mSessions")
793a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
803a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
819a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey    /** Historical sessions kept around for debugging purposes */
829a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey    @GuardedBy("mSessions")
839a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey    private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
849a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey
8516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private RemoteCallbackList<IPackageInstallerCallback> mCallbacks = new RemoteCallbackList<>();
86a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
87ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    private static final FilenameFilter sStageFilter = new FilenameFilter() {
88ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        @Override
89ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        public boolean accept(File dir, String name) {
90ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            return name.startsWith("vmdl") && name.endsWith(".tmp");
91ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
92ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    };
93ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
943a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
953a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mContext = context;
963a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mPm = pm;
973a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
983a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
993a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mStagingDir = stagingDir;
100ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
101ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        mInstallThread = new HandlerThread(TAG);
102ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        mInstallThread.start();
1033a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1043a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        synchronized (mSessions) {
1053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            readSessionsLocked();
1063a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1073a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            // Clean up orphaned staging directories
108ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
1093a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            for (int i = 0; i < mSessions.size(); i++) {
110ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                final PackageInstallerSession session = mSessions.valueAt(i);
111ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                stages.remove(session.sessionStageDir);
112ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            }
113ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            for (File stage : stages) {
114ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                Slog.w(TAG, "Deleting orphan stage " + stage);
115ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                if (stage.isDirectory()) {
116ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                    FileUtils.deleteContents(stage);
117ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                }
118ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                stage.delete();
1193a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
120ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
121ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    }
122ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
123ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    @Deprecated
124ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    public File allocateSessionDir() throws IOException {
125ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        synchronized (mSessions) {
126ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            try {
127ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                final int sessionId = allocateSessionIdLocked();
128ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                return prepareSessionStageDir(sessionId);
129ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            } catch (IllegalStateException e) {
130ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey                throw new IOException(e);
1313a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
1323a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1333a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1343a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1353a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private void readSessionsLocked() {
1363a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        // TODO: implement persisting
1373a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mSessions.clear();
1383a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mNextSessionId = 1;
1393a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1403a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1413a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private void writeSessionsLocked() {
1423a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        // TODO: implement persisting
1433a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1443a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1453a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private void writeSessionsAsync() {
1463a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        IoThread.getHandler().post(new Runnable() {
1473a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            @Override
1483a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            public void run() {
1493a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                synchronized (mSessions) {
1503a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                    writeSessionsLocked();
1513a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                }
1523a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
1533a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        });
1543a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1553a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1563a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @Override
15716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public int createSession(InstallSessionParams params, String installerPackageName, int userId) {
1583a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        final int callingUid = Binder.getCallingUid();
159a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
1603a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1613a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        if (mPm.isUserRestricted(UserHandle.getUserId(callingUid),
1623a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                UserManager.DISALLOW_INSTALL_APPS)) {
1633a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw new SecurityException("User restriction prevents installing");
1643a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1653a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1663a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) {
1673a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            params.installFlags |= INSTALL_FROM_ADB;
1683a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        } else {
169ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            mAppOps.checkPackage(callingUid, installerPackageName);
170ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
1713a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            params.installFlags &= ~INSTALL_FROM_ADB;
1723a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            params.installFlags &= ~INSTALL_ALL_USERS;
1733a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            params.installFlags |= INSTALL_REPLACE_EXISTING;
1743a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1753a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
17616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        switch (params.mode) {
17716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            case InstallSessionParams.MODE_FULL_INSTALL:
17816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            case InstallSessionParams.MODE_INHERIT_EXISTING:
17916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                break;
18016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            default:
18116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                throw new IllegalArgumentException("Params must have valid mode set");
1826c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        }
1836c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey
184a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        // Sanity check that install could fit
18516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        if (params.sizeBytes > 0) {
186a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            try {
18716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                mPm.freeStorage(params.sizeBytes);
188a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            } catch (IOException e) {
189a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                throw ExceptionUtils.wrap(e);
190a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
191a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
192a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
193a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        final int sessionId;
194a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        final PackageInstallerSession session;
1953a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        synchronized (mSessions) {
196a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            sessionId = allocateSessionIdLocked();
197a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
1983a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            final long createdMillis = System.currentTimeMillis();
199ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            final File sessionStageDir = prepareSessionStageDir(sessionId);
2003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
201a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            session = new PackageInstallerSession(mCallback, mPm, sessionId, userId,
202a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                    installerPackageName, callingUid, params, createdMillis, sessionStageDir,
203a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                    mInstallThread.getLooper());
2043a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            mSessions.put(sessionId, session);
2053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
206a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
207a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        notifySessionCreated(session.generateInfo());
208a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        writeSessionsAsync();
209a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        return sessionId;
2103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
2113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
2123a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @Override
2133a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    public IPackageInstallerSession openSession(int sessionId) {
2143a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        synchronized (mSessions) {
2153a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            final PackageInstallerSession session = mSessions.get(sessionId);
2163a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            if (session == null) {
2173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                throw new IllegalStateException("Missing session " + sessionId);
2183a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
2193a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            if (Binder.getCallingUid() != session.installerUid) {
2203a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                throw new SecurityException("Caller has no access to session " + sessionId);
2213a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
2223a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            return session;
2233a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
2243a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
2253a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
2263a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private int allocateSessionIdLocked() {
2273a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        if (mSessions.get(mNextSessionId) != null) {
2283a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw new IllegalStateException("Next session already allocated");
2293a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
2303a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        return mNextSessionId++;
2313a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
2323a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
233ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    private File prepareSessionStageDir(int sessionId) {
234ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
235ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
236ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        if (file.exists()) {
237ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            throw new IllegalStateException();
238ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
239ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
240ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        try {
241ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            Os.mkdir(file.getAbsolutePath(), 0755);
242ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            Os.chmod(file.getAbsolutePath(), 0755);
243ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        } catch (ErrnoException e) {
244ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            // This purposefully throws if directory already exists
245ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            throw new IllegalStateException("Failed to prepare session dir", e);
246ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
247ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
248ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        if (!SELinux.restorecon(file)) {
249ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            throw new IllegalStateException("Failed to prepare session dir");
250ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        }
251ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
252ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        return file;
253ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    }
254ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey
2553a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @Override
25616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public InstallSessionInfo getSessionInfo(int sessionId) {
25716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        synchronized (mSessions) {
25816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final PackageInstallerSession session = mSessions.get(sessionId);
25916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final boolean isOwner = (session != null)
26016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    && (session.installerUid == Binder.getCallingUid());
26116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            if (!isOwner) {
26216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                enforceCallerCanReadSessions();
26316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
26416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return session != null ? session.generateInfo() : null;
26516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
26616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
26716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
26816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    @Override
26916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public List<InstallSessionInfo> getAllSessions(int userId) {
27016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
27116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        enforceCallerCanReadSessions();
2723a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
273bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        final List<InstallSessionInfo> result = new ArrayList<>();
2743a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        synchronized (mSessions) {
2753a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            for (int i = 0; i < mSessions.size(); i++) {
2763a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                final PackageInstallerSession session = mSessions.valueAt(i);
277bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey                if (session.userId == userId) {
278bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey                    result.add(session.generateInfo());
2793a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                }
2803a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
2813a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
282bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        return result;
2833a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
2843a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
2853a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @Override
28616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) {
28716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions");
28816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
28916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
29016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final List<InstallSessionInfo> result = new ArrayList<>();
29116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        synchronized (mSessions) {
29216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            for (int i = 0; i < mSessions.size(); i++) {
29316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                final PackageInstallerSession session = mSessions.valueAt(i);
29416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                if (Objects.equals(session.installerPackageName, installerPackageName)
29516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                        && session.userId == userId) {
29616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    result.add(session.generateInfo());
29716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                }
29816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
29916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
30016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        return result;
30116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
30216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
30316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    @Override
304bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    public void uninstall(String packageName, int flags, IPackageDeleteObserver observer,
305ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            int userId) {
306a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
30716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
30816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        // TODO: enforce installer of record or permission
309bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        mPm.deletePackageAsUser(packageName, observer, userId, flags);
3103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
3113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3123a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    @Override
313ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    public void uninstallSplit(String basePackageName, String overlayName, int flags,
314ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            IPackageDeleteObserver observer, int userId) {
315a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit");
316a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
3173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        // TODO: flesh out once PM has split support
3183a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        throw new UnsupportedOperationException();
3193a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
3203a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
321bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    @Override
32216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void registerCallback(IPackageInstallerCallback callback, int userId) {
32316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
32416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        enforceCallerCanReadSessions();
325a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
32616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mCallbacks.register(callback, new UserHandle(userId));
327bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
328bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
329bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    @Override
33016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void unregisterCallback(IPackageInstallerCallback callback) {
33116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mCallbacks.unregister(callback);
332a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    }
333a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
334a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    private int getSessionUserId(int sessionId) {
335a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        synchronized (mSessions) {
336a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            return UserHandle.getUserId(mSessions.get(sessionId).installerUid);
337a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
338a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    }
339a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
34016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    /**
34116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * We allow those with permission, or the current home app.
34216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     */
34316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private void enforceCallerCanReadSessions() {
34416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final boolean hasPermission = (mContext.checkCallingOrSelfPermission(
34516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                android.Manifest.permission.READ_INSTALL_SESSIONS)
34616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                == PackageManager.PERMISSION_GRANTED);
34716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final boolean isHomeApp = mPm.checkCallerIsHomeApp();
34816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        if (hasPermission || isHomeApp) {
34916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return;
35016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        } else {
35116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            throw new SecurityException("Caller must be current home app to read install sessions");
35216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
35316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
35416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
355a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    private void notifySessionCreated(InstallSessionInfo info) {
356a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        final int userId = getSessionUserId(info.sessionId);
35716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final int n = mCallbacks.beginBroadcast();
358a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        for (int i = 0; i < n; i++) {
35916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
36016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
36116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            // TODO: dispatch notifications for slave profiles
362a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            if (userId == user.getIdentifier()) {
363a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                try {
36416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    callback.onSessionCreated(info.sessionId);
365a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                } catch (RemoteException ignored) {
366a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                }
367a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
368a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
36916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mCallbacks.finishBroadcast();
370a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    }
371a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
37216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private void notifySessionProgressChanged(int sessionId, float progress) {
373a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        final int userId = getSessionUserId(sessionId);
37416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final int n = mCallbacks.beginBroadcast();
375a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        for (int i = 0; i < n; i++) {
37616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
37716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
378a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            if (userId == user.getIdentifier()) {
379a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                try {
38016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    callback.onSessionProgressChanged(sessionId, progress);
381a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                } catch (RemoteException ignored) {
382a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                }
383a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
384a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
38516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mCallbacks.finishBroadcast();
386a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    }
387a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
388a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    private void notifySessionFinished(int sessionId, boolean success) {
389a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        final int userId = getSessionUserId(sessionId);
39016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final int n = mCallbacks.beginBroadcast();
391a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        for (int i = 0; i < n; i++) {
39216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
39316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
394a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            if (userId == user.getIdentifier()) {
395a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                try {
39616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    callback.onSessionFinished(sessionId, success);
397a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                } catch (RemoteException ignored) {
398a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                }
399a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
400a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
40116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        mCallbacks.finishBroadcast();
402a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    }
403a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
404a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey    void dump(IndentingPrintWriter pw) {
405a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        synchronized (mSessions) {
4069a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.println("Active install sessions:");
4079a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.increaseIndent();
4089a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            int N = mSessions.size();
409a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            for (int i = 0; i < N; i++) {
410a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                final PackageInstallerSession session = mSessions.valueAt(i);
411a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                session.dump(pw);
412a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                pw.println();
413a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
4149a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.println();
4159a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.decreaseIndent();
4169a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey
4179a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.println("Historical install sessions:");
4189a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.increaseIndent();
4199a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            N = mHistoricalSessions.size();
4209a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            for (int i = 0; i < N; i++) {
4219a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
4229a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey                session.dump(pw);
4239a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey                pw.println();
4249a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            }
4259a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.println();
4269a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey            pw.decreaseIndent();
427a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
428bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
429bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
4303a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    class Callback {
43116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
43216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            notifySessionProgressChanged(session.sessionId, progress);
4333a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4343a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
435a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        public void onSessionFinished(PackageInstallerSession session, boolean success) {
436a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            notifySessionFinished(session.sessionId, success);
437a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            synchronized (mSessions) {
438a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                mSessions.remove(session.sessionId);
4399a445771f57dd15b06db0dbefd66c368d84eec2dJeff Sharkey                mHistoricalSessions.put(session.sessionId, session);
440a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
4413a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            writeSessionsAsync();
4423a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4433a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
4443a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey}
445