301 lines
11 KiB
Java
301 lines
11 KiB
Java
![]() |
/*
|
||
|
* 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 android.os.image;
|
||
|
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.RequiresPermission;
|
||
|
import android.annotation.SystemService;
|
||
|
import android.content.Context;
|
||
|
import android.gsi.AvbPublicKey;
|
||
|
import android.gsi.GsiProgress;
|
||
|
import android.gsi.IGsiService;
|
||
|
import android.os.ParcelFileDescriptor;
|
||
|
import android.os.RemoteException;
|
||
|
import android.util.Pair;
|
||
|
|
||
|
/**
|
||
|
* The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the
|
||
|
* installation, the device can reboot into this image with a new created /data. This image will
|
||
|
* last until the next reboot and then the device will go back to the original image. However the
|
||
|
* installed image and the new created /data are not deleted but disabled. Thus the application can
|
||
|
* either re-enable the installed image by calling {@link #toggle} or use the {@link #remove} to
|
||
|
* delete it completely. In other words, there are three device states: no installation, installed
|
||
|
* and running. The procedure to install a DynamicSystem starts with a {@link #startInstallation},
|
||
|
* followed by a series of {@link #write} and ends with a {@link commit}. Once the installation is
|
||
|
* complete, the device state changes from no installation to the installed state and a followed
|
||
|
* reboot will change its state to running. Note one instance of DynamicSystem can exist on a given
|
||
|
* device thus the {@link #startInstallation} will fail if the device is currently running a
|
||
|
* DynamicSystem.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemService(Context.DYNAMIC_SYSTEM_SERVICE)
|
||
|
public class DynamicSystemManager {
|
||
|
private static final String TAG = "DynamicSystemManager";
|
||
|
|
||
|
private final IDynamicSystemService mService;
|
||
|
|
||
|
/** {@hide} */
|
||
|
public DynamicSystemManager(IDynamicSystemService service) {
|
||
|
mService = service;
|
||
|
}
|
||
|
|
||
|
/** The DynamicSystemManager.Session represents a started session for the installation. */
|
||
|
public class Session {
|
||
|
private Session() {}
|
||
|
|
||
|
/**
|
||
|
* Set the file descriptor that points to a ashmem which will be used
|
||
|
* to fetch data during the submitFromAshmem.
|
||
|
*
|
||
|
* @param ashmem fd that points to a ashmem
|
||
|
* @param size size of the ashmem file
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
|
||
|
try {
|
||
|
return mService.setAshmem(ashmem, size);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Submit bytes to the DSU partition from the ashmem previously set with
|
||
|
* setAshmem.
|
||
|
*
|
||
|
* @param size Number of bytes
|
||
|
* @return true on success, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean submitFromAshmem(int size) {
|
||
|
try {
|
||
|
return mService.submitFromAshmem(size);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve AVB public key from installing partition.
|
||
|
*
|
||
|
* @param dst Output the AVB public key.
|
||
|
* @return true on success, false if partition doesn't have a
|
||
|
* valid VBMeta block to retrieve the AVB key from.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean getAvbPublicKey(AvbPublicKey dst) {
|
||
|
try {
|
||
|
return mService.getAvbPublicKey(dst);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Finish write and make device to boot into the it after reboot.
|
||
|
*
|
||
|
* @return {@code true} if the call succeeds. {@code false} if there is any native runtime
|
||
|
* error.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean commit() {
|
||
|
try {
|
||
|
return mService.setEnable(true, true);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Start DynamicSystem installation.
|
||
|
*
|
||
|
* @return true if the call succeeds
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean startInstallation(String dsuSlot) {
|
||
|
try {
|
||
|
return mService.startInstallation(dsuSlot);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
|
||
|
* may use another thread to call the getStartProgress() to get the progress.
|
||
|
*
|
||
|
* @param name The DSU partition name
|
||
|
* @param size Size of the DSU image in bytes
|
||
|
* @param readOnly True if the partition is read only, e.g. system.
|
||
|
* @return {@code Integer} an IGsiService.INSTALL_* status code. {@link Session} an installation
|
||
|
* session object if successful, otherwise {@code null}.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public @NonNull Pair<Integer, Session> createPartition(
|
||
|
String name, long size, boolean readOnly) {
|
||
|
try {
|
||
|
int status = mService.createPartition(name, size, readOnly);
|
||
|
if (status == IGsiService.INSTALL_OK) {
|
||
|
return new Pair<>(status, new Session());
|
||
|
} else {
|
||
|
return new Pair<>(status, null);
|
||
|
}
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Complete the current partition installation.
|
||
|
*
|
||
|
* @return true if the partition installation completes without error.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean closePartition() {
|
||
|
try {
|
||
|
return mService.closePartition();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Finish a previously started installation. Installations without a corresponding
|
||
|
* finishInstallation() will be cleaned up during device boot.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean finishInstallation() {
|
||
|
try {
|
||
|
return mService.finishInstallation();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Query the progress of the current installation operation. This can be called while the
|
||
|
* installation is in progress.
|
||
|
*
|
||
|
* @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
|
||
|
* status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
|
||
|
* IGsiService.STATUS_COMPLETE.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public GsiProgress getInstallationProgress() {
|
||
|
try {
|
||
|
return mService.getInstallationProgress();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Abort the installation process. Note this method must be called in a thread other than the
|
||
|
* one calling the startInstallation method as the startInstallation method will not return
|
||
|
* until it is finished.
|
||
|
*
|
||
|
* @return {@code true} if the call succeeds. {@code false} if there is no installation
|
||
|
* currently.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean abort() {
|
||
|
try {
|
||
|
return mService.abort();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @return {@code true} if the device is running a dynamic system */
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean isInUse() {
|
||
|
try {
|
||
|
return mService.isInUse();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @return {@code true} if the device has a dynamic system installed */
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean isInstalled() {
|
||
|
try {
|
||
|
return mService.isInstalled();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @return {@code true} if the device has a dynamic system enabled */
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean isEnabled() {
|
||
|
try {
|
||
|
return mService.isEnabled();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove DynamicSystem installation if present
|
||
|
*
|
||
|
* @return {@code true} if the call succeeds. {@code false} if there is no installed image.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean remove() {
|
||
|
try {
|
||
|
return mService.remove();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable or disable DynamicSystem.
|
||
|
* @return {@code true} if the call succeeds. {@code false} if there is no installed image.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public boolean setEnable(boolean enable, boolean oneShot) {
|
||
|
try {
|
||
|
return mService.setEnable(enable, oneShot);
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the suggested scratch partition size for overlayFS.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public long suggestScratchSize() {
|
||
|
try {
|
||
|
return mService.suggestScratchSize();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the active DSU slot
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
|
||
|
public String getActiveDsuSlot() {
|
||
|
try {
|
||
|
return mService.getActiveDsuSlot();
|
||
|
} catch (RemoteException e) {
|
||
|
throw new RuntimeException(e.toString());
|
||
|
}
|
||
|
}
|
||
|
}
|