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

Commit f17f42b0 authored by Howard Chen's avatar Howard Chen
Browse files

Add the DynamicAndroid Service

  Define the DynamicAndroid with AIDL.
  Add a java implementation.
  Start a service instance in the system server.
  Add a permission test.

Bug: 122015653
Test: Build & Test on a pixel phone with following command \
  ./frameworks/base/services/tests/runtests.py -e class com.android.server.DynamicAndroidTest

Merged-In: I2e54b6b71fac4a4c5a9c9c25ce6bdac74cddcfb7
Change-Id: I2e54b6b71fac4a4c5a9c9c25ce6bdac74cddcfb7
parent 8b194fe9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ java_defaults {
        "core/java/android/os/ICancellationSignal.aidl",
        "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
        "core/java/android/os/IDeviceIdleController.aidl",
        "core/java/android/os/IDynamicAndroidService.aidl",
        "core/java/android/os/IHardwarePropertiesManager.aidl",
        ":libincident_aidl",
        "core/java/android/os/IMaintenanceActivityListener.aidl",
@@ -601,6 +602,7 @@ java_defaults {

        ":storaged_aidl",
        ":vold_aidl",
        ":gsiservice_aidl",
        ":installd_aidl",
        ":dumpstate_aidl",

@@ -657,6 +659,7 @@ java_defaults {
            "frameworks/native/aidl/gui",
            "system/core/storaged/binder",
            "system/vold/binder",
            "system/gsid/aidl",
            "system/bt/binder",
            "system/security/keystore/binder",
        ],
+1 −0
Original line number Diff line number Diff line
@@ -837,6 +837,7 @@ package android.content {
    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
    field public static final String BACKUP_SERVICE = "backup";
    field public static final String CONTEXTHUB_SERVICE = "contexthub";
    field public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";
    field public static final String EUICC_CARD_SERVICE = "euicc_card";
    field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
    field public static final String NETD_SERVICE = "netd";
+12 −0
Original line number Diff line number Diff line
@@ -114,11 +114,13 @@ import android.os.BugreportManager;
import android.os.Build;
import android.os.DeviceIdleManager;
import android.os.DropBoxManager;
import android.os.DynamicAndroidManager;
import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.IDumpstate;
import android.os.IDynamicAndroidService;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
@@ -1065,6 +1067,16 @@ final class SystemServiceRegistry {
                            throws ServiceNotFoundException {
                        return new TimeZoneDetector();
                    }});
        registerService(Context.DYNAMIC_ANDROID_SERVICE, DynamicAndroidManager.class,
                new CachedServiceFetcher<DynamicAndroidManager>() {
                    @Override
                    public DynamicAndroidManager createService(ContextImpl ctx)
                            throws ServiceNotFoundException {
                        IBinder b = ServiceManager.getServiceOrThrow(
                                Context.DYNAMIC_ANDROID_SERVICE);
                        return new DynamicAndroidManager(
                                IDynamicAndroidService.Stub.asInterface(b));
                    }});
    }

    /**
+8 −0
Original line number Diff line number Diff line
@@ -4304,6 +4304,14 @@ public abstract class Context {
     */
    public static final String TELEPHONY_RCS_SERVICE = "ircs";

     /**
     * Use with {@link #getSystemService(String)} to retrieve an
     * {@link android.os.DynamicAndroidManager}.
     * @hide
     */
    @SystemApi
    public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";

    /**
     * Determine whether the given permission is allowed for a particular
     * process and user ID running in the system.
+188 −0
Original line number Diff line number Diff line
/*
 * 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;

import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.gsi.GsiProgress;

/**
 * The DynamicAndroidManager offers a mechanism to use a new Android 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 DynamicAndroid 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 dynamic android can exist on a
 * given device thus the {@link #startInstallation} will fail if the device is currently running a
 * DynamicAndroid.
 *
 * @hide
 */
@SystemService(Context.DYNAMIC_ANDROID_SERVICE)
public class DynamicAndroidManager {
    private static final String TAG = "DynamicAndroidManager";

    private final IDynamicAndroidService mService;

    /** {@hide} */
    public DynamicAndroidManager(IDynamicAndroidService service) {
        mService = service;
    }

    /** The DynamicAndroidManager.Session represents a started session for the installation. */
    public class Session {
        private Session() {}
        /**
         * Write a chunk of the DynamicAndroid system image
         *
         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
         *     error.
         */
        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
        public boolean write(byte[] buf) {
            try {
                return mService.write(buf);
            } 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_ANDROID)
        public boolean commit() {
            try {
                return mService.commit();
            } catch (RemoteException e) {
                throw new RuntimeException(e.toString());
            }
        }
    }
    /**
     * Start DynamicAndroid 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 systemSize system size in bytes
     * @param userdataSize userdata size in bytes
     * @return {@code true} if the call succeeds. {@code false} either the device does not contain
     *     enough space or a DynamicAndroid is currently in use where the {@link #isInUse} would be
     *     true.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
    public Session startInstallation(long systemSize, long userdataSize) {
        try {
            if (mService.startInstallation(systemSize, userdataSize)) {
                return new Session();
            } else {
                return null;
            }
        } 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_ANDROID)
    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_ANDROID)
    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 android */
    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
    public boolean isInUse() {
        try {
            return mService.isInUse();
        } catch (RemoteException e) {
            throw new RuntimeException(e.toString());
        }
    }

    /** @return {@code true} if the device has a dynamic android installed */
    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
    public boolean isInstalled() {
        try {
            return mService.isInstalled();
        } catch (RemoteException e) {
            throw new RuntimeException(e.toString());
        }
    }

    /**
     * Remove DynamicAndroid installation if present
     *
     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
    public boolean remove() {
        try {
            return mService.remove();
        } catch (RemoteException e) {
            throw new RuntimeException(e.toString());
        }
    }

    /**
     * Enable DynamicAndroid when it's not enabled, otherwise, disable it.
     *
     * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
    public boolean toggle() {
        try {
            return mService.toggle();
        } catch (RemoteException e) {
            throw new RuntimeException(e.toString());
        }
    }
}
Loading