Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be98c3f9 authored by Dario Freni's avatar Dario Freni
Browse files

First version of Staging Manager.

This CL introduces a new class called Staging Manager, which PackageInstaller
uses to delegate the management of staged session, i.e. to coordinate with
apexd and PackageManager to perform package verification and
installation at reboot.

In this first version I ported a mock implementation of the staging
session management.

Bug: 118865310
Fix: 122072686
Test: Used a small app to query getStagedSessions and to receive the
broadcast messaged. Installed package with adb install --staged.
Change-Id: Iaeda1e8bee8d375b78bb0156baa8e7e53e5e23c1
parent 2f16d51c
Loading
Loading
Loading
Loading
+11 −29
Original line number Original line Diff line number Diff line
@@ -128,6 +128,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements


    private final Context mContext;
    private final Context mContext;
    private final PackageManagerService mPm;
    private final PackageManagerService mPm;
    private final StagingManager mStagingManager;
    private final PermissionManagerInternal mPermissionManager;
    private final PermissionManagerInternal mPermissionManager;


    private AppOpsManager mAppOps;
    private AppOpsManager mAppOps;
@@ -163,12 +164,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
    @GuardedBy("mSessions")
    @GuardedBy("mSessions")
    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();


    // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
    //           shouldn't be needed at all.
    // TODO(b/118865310): Implement staged sessions logic.
    @GuardedBy("mStagedSessions")
    private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();

    /** Historical sessions kept around for debugging purposes */
    /** Historical sessions kept around for debugging purposes */
    @GuardedBy("mSessions")
    @GuardedBy("mSessions")
    private final List<String> mHistoricalSessions = new ArrayList<>();
    private final List<String> mHistoricalSessions = new ArrayList<>();
@@ -204,6 +199,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                "package-session");
                "package-session");
        mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
        mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
        mSessionsDir.mkdirs();
        mSessionsDir.mkdirs();

        mStagingManager = new StagingManager(pm);
    }
    }


    public void systemReady() {
    public void systemReady() {
@@ -311,7 +308,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                        final PackageInstallerSession session;
                        final PackageInstallerSession session;
                        try {
                        try {
                            session = PackageInstallerSession.readFromXml(in, mInternalCallback,
                            session = PackageInstallerSession.readFromXml(in, mInternalCallback,
                                    mContext, mPm, mInstallThread.getLooper(), mSessionsDir, this);
                                    mContext, mPm, mInstallThread.getLooper(), mStagingManager,
                                    mSessionsDir, this);
                            currentSession = session;
                            currentSession = session;
                        } catch (Exception e) {
                        } catch (Exception e) {
                            currentSession = null;
                            currentSession = null;
@@ -536,17 +534,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
            }
            }
        }
        }
        session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
        session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                mInstallThread.getLooper(), sessionId, userId, installerPackageName,
                mInstallThread.getLooper(), mStagingManager, sessionId, userId,
                callingUid, params, createdMillis, stageDir, stageCid, false, false, null,
                installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
                SessionInfo.INVALID_ID);
                false, null, SessionInfo.INVALID_ID);


        synchronized (mSessions) {
        synchronized (mSessions) {
            mSessions.put(sessionId, session);
            mSessions.put(sessionId, session);
        }
        }
        if (params.isStaged) {
        if (params.isStaged) {
            synchronized (mStagedSessions) {
            mStagingManager.createSession(session);
                mStagedSessions.put(sessionId, session);
            }
        }
        }


        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
@@ -679,14 +675,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements


    @Override
    @Override
    public ParceledListSlice<SessionInfo> getStagedSessions() {
    public ParceledListSlice<SessionInfo> getStagedSessions() {
        final List<SessionInfo> result = new ArrayList<>();
        return mStagingManager.getSessions();
        synchronized (mStagedSessions) {
            for (int i = 0; i < mStagedSessions.size(); i++) {
                final PackageInstallerSession session = mStagedSessions.valueAt(i);
                result.add(session.generateInfo(false));
            }
        }
        return new ParceledListSlice<>(result);
    }
    }


    @Override
    @Override
@@ -1131,16 +1120,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
            mInstallHandler.post(new Runnable() {
            mInstallHandler.post(new Runnable() {
                @Override
                @Override
                public void run() {
                public void run() {
                    // TODO(b/118865310): remove this mock implementation.
                    if (session.isStaged()) {
                    if (session.isStaged()) {
                        // If the session is aborted, don't keep it in memory. Only store
                        // sessions successfully staged.
                        if (!success) {
                        if (!success) {
                            synchronized (mStagedSessions) {
                            mStagingManager.abortSession(session);
                                mStagedSessions.remove(session.sessionId);
                            }
                        } else {
                            return;
                        }
                        }
                    }
                    }
                    synchronized (mSessions) {
                    synchronized (mSessions) {
+17 −8
Original line number Original line Diff line number Diff line
@@ -174,6 +174,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private final PackageManagerService mPm;
    private final PackageManagerService mPm;
    private final Handler mHandler;
    private final Handler mHandler;
    private final PackageSessionProvider mSessionProvider;
    private final PackageSessionProvider mSessionProvider;
    private final StagingManager mStagingManager;


    final int sessionId;
    final int sessionId;
    final int userId;
    final int userId;
@@ -396,7 +397,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {


    public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
    public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
            Context context, PackageManagerService pm,
            Context context, PackageManagerService pm,
            PackageSessionProvider sessionProvider, Looper looper,
            PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
            int sessionId, int userId,
            int sessionId, int userId,
            String installerPackageName, int installerUid, SessionParams params, long createdMillis,
            String installerPackageName, int installerUid, SessionParams params, long createdMillis,
            File stageDir, String stageCid, boolean prepared, boolean sealed,
            File stageDir, String stageCid, boolean prepared, boolean sealed,
@@ -406,6 +407,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        mPm = pm;
        mPm = pm;
        mSessionProvider = sessionProvider;
        mSessionProvider = sessionProvider;
        mHandler = new Handler(looper, mHandlerCallback);
        mHandler = new Handler(looper, mHandlerCallback);
        mStagingManager = stagingManager;


        this.sessionId = sessionId;
        this.sessionId = sessionId;
        this.userId = userId;
        this.userId = userId;
@@ -1063,9 +1065,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            return;
            return;
        }
        }
        if (isStaged()) {
        if (isStaged()) {
            // STOPSHIP: implement staged sessions
            mStagingManager.commitSession(this);
            mStagedSessionReady = true;
            mPm.sendSessionUpdatedBroadcast(generateInfo(), userId);
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
            return;
        }
        }
@@ -1083,7 +1083,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    if (activeSession != null) {
                    if (activeSession != null) {
                        if ((activeSession.getSessionParams().installFlags
                        if ((activeSession.getSessionParams().installFlags
                                & PackageManager.INSTALL_APEX) != 0) {
                                & PackageManager.INSTALL_APEX) != 0) {
                            // TODO(b/118865310): Add exception to this case for staged installs
                            throw new PackageManagerException(
                            throw new PackageManagerException(
                                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                                    "Atomic install is not supported for APEX packages.");
                                    "Atomic install is not supported for APEX packages.");
@@ -1950,6 +1949,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        mCallback.onSessionFinished(this, success);
        mCallback.onSessionFinished(this, success);
    }
    }


    void setStagedSessionReady() {
        synchronized (mLock) {
            mStagedSessionReady = true;
            mStagedSessionApplied = false;
            mStagedSessionFailed = false;
            mStagedSessionErrorCode = SessionInfo.NO_ERROR;
        }
    }

    private void destroyInternal() {
    private void destroyInternal() {
        synchronized (mLock) {
        synchronized (mLock) {
            mSealed = true;
            mSealed = true;
@@ -2151,7 +2159,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    //                       can have a complete session for the constructor
    //                       can have a complete session for the constructor
    public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
    public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir,
            @NonNull PackageManagerService pm, Looper installerThread,
            @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
            @NonNull PackageSessionProvider sessionProvider)
            @NonNull PackageSessionProvider sessionProvider)
            throws IOException, XmlPullParserException {
            throws IOException, XmlPullParserException {
        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
@@ -2195,8 +2204,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            params.appIconLastModified = appIconFile.lastModified();
            params.appIconLastModified = appIconFile.lastModified();
        }
        }
        return new PackageInstallerSession(callback, context, pm, sessionProvider,
        return new PackageInstallerSession(callback, context, pm, sessionProvider,
                installerThread, sessionId, userId, installerPackageName, installerUid,
                installerThread, stagingManager, sessionId, userId, installerPackageName,
                params, createdMillis, stageDir, stageCid, prepared, sealed,
                installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
                EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
                EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
    }
    }


+90 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import android.annotation.NonNull;
import android.content.pm.PackageInstaller;
import android.content.pm.ParceledListSlice;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.List;

/**
 * This class handles staged install sessions, i.e. install sessions that require packages to
 * be installed only after a reboot.
 */
public class StagingManager {

    private static final String TAG = "StagingManager";

    private final PackageManagerService mPm;

    // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
    //           shouldn't be needed at all.
    // TODO(b/118865310): Implement staged sessions logic.
    @GuardedBy("mStagedSessions")
    private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();

    StagingManager(PackageManagerService pm) {
        mPm = pm;
    }

    private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
        synchronized (mStagedSessions) {
            PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
            if (storedSession == null) {
                throw new IllegalStateException("Attempting to change state of a session not "
                        + "known to StagingManager");
            }
            mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
        }
    }

    ParceledListSlice<PackageInstaller.SessionInfo> getSessions() {
        final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
        synchronized (mStagedSessions) {
            for (int i = 0; i < mStagedSessions.size(); i++) {
                result.add(mStagedSessions.valueAt(i).generateInfo(false));
            }
        }
        return new ParceledListSlice<>(result);
    }

    void commitSession(@NonNull PackageInstallerSession sessionInfo) {
        updateStoredSession(sessionInfo);
        // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
        //                    now we directly mark it as ready.
        sessionInfo.setStagedSessionReady();
        mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
    }

    void createSession(@NonNull PackageInstallerSession sessionInfo) {
        synchronized (mStagedSessions) {
            mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
        }
    }

    void abortSession(@NonNull PackageInstallerSession sessionInfo) {
        updateStoredSession(sessionInfo);
        synchronized (mStagedSessions) {
            mStagedSessions.remove(sessionInfo.sessionId);
        }
    }
}