| /* |
| * 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()); |
| } |
| } |
| } |