Java程序  |  206行  |  6.82 KB

/*
 * Copyright (C) 2019 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;

import android.content.Context;
import android.content.pm.PackageManager;
import android.gsi.GsiInstallParams;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.os.Environment;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.util.Slog;

import java.io.File;

/**
 * DynamicSystemService implements IDynamicSystemService. It provides permission check before
 * passing requests to gsid
 */
public class DynamicSystemService extends IDynamicSystemService.Stub implements DeathRecipient {
    private static final String TAG = "DynamicSystemService";
    private static final String NO_SERVICE_ERROR = "no gsiservice";
    private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
    private static final String PATH_DEFAULT = "/data/gsi";
    private Context mContext;
    private volatile IGsiService mGsiService;

    DynamicSystemService(Context context) {
        mContext = context;
    }

    private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
        IBinder binder = ServiceManager.getService("gsiservice");
        if (binder == null) {
            return null;
        }
        /**
         * The init will restart gsiservice if it crashed and the proxy object will need to be
         * re-initialized in this case.
         */
        binder.linkToDeath(recipient, 0);
        return IGsiService.Stub.asInterface(binder);
    }

    /** implements DeathRecipient */
    @Override
    public void binderDied() {
        Slog.w(TAG, "gsiservice died; reconnecting");
        synchronized (this) {
            mGsiService = null;
        }
    }

    private IGsiService getGsiService() throws RemoteException {
        checkPermission();

        if (!"running".equals(SystemProperties.get("init.svc.gsid"))) {
            SystemProperties.set("ctl.start", "gsid");
        }

        for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
            synchronized (this) {
                if (mGsiService == null) {
                    mGsiService = connect(this);
                }
                if (mGsiService != null) {
                    return mGsiService;
                }
            }

            try {
                Slog.d(TAG, "GsiService is not ready, wait for " + sleepMs + "ms");
                Thread.sleep(sleepMs);
            } catch (InterruptedException e) {
                Slog.e(TAG, "Interrupted when waiting for GSID");
                return null;
            }
        }

        throw new RemoteException(NO_SERVICE_ERROR);
    }

    private void checkPermission() {
        if (mContext.checkCallingOrSelfPermission(
                        android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires MANAGE_DYNAMIC_SYSTEM permission");
        }
    }

    @Override
    public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
        // priority from high to low: sysprop -> sdcard -> /data
        String path = SystemProperties.get("os.aot.path");
        if (path.isEmpty()) {
            final int userId = UserHandle.myUserId();
            final StorageVolume[] volumes =
                    StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE);
            for (StorageVolume volume : volumes) {
                if (volume.isEmulated()) continue;
                if (!volume.isRemovable()) continue;
                if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue;
                File sdCard = volume.getPathFile();
                if (sdCard.isDirectory()) {
                    path = sdCard.getPath();
                    break;
                }
            }
            if (path.isEmpty()) {
                path = PATH_DEFAULT;
            }
            Slog.i(TAG, "startInstallation -> " + path);
        }
        GsiInstallParams installParams = new GsiInstallParams();
        installParams.installDir = path;
        installParams.gsiSize = systemSize;
        installParams.userdataSize = userdataSize;
        return getGsiService().beginGsiInstall(installParams) == 0;
    }

    @Override
    public GsiProgress getInstallationProgress() throws RemoteException {
        return getGsiService().getInstallProgress();
    }

    @Override
    public boolean abort() throws RemoteException {
        return getGsiService().cancelGsiInstall();
    }

    @Override
    public boolean isInUse() throws RemoteException {
        boolean gsidWasRunning = "running".equals(SystemProperties.get("init.svc.gsid"));
        boolean isInUse = false;

        try {
            isInUse = getGsiService().isGsiRunning();
        } finally {
            if (!gsidWasRunning && !isInUse) {
                SystemProperties.set("ctl.stop", "gsid");
            }
        }

        return isInUse;
    }

    @Override
    public boolean isInstalled() throws RemoteException {
        return getGsiService().isGsiInstalled();
    }

    @Override
    public boolean isEnabled() throws RemoteException {
        return getGsiService().isGsiEnabled();
    }

    @Override
    public boolean remove() throws RemoteException {
        return getGsiService().removeGsiInstall();
    }

    @Override
    public boolean setEnable(boolean enable) throws RemoteException {
        IGsiService gsiService = getGsiService();
        if (enable) {
            final int status = gsiService.getGsiBootStatus();
            final boolean singleBoot = (status == IGsiService.BOOT_STATUS_SINGLE_BOOT);
            return gsiService.setGsiBootable(singleBoot) == 0;
        } else {
            return gsiService.disableGsiInstall();
        }
    }

    @Override
    public boolean write(byte[] buf) throws RemoteException {
        return getGsiService().commitGsiChunkFromMemory(buf);
    }

    @Override
    public boolean commit() throws RemoteException {
        return getGsiService().setGsiBootable(true) == 0;
    }
}