Merge "Add the DynamicAndroid Service"

This commit is contained in:
Howard Chen
2019-01-31 14:35:49 +00:00
committed by Gerrit Code Review
11 changed files with 492 additions and 0 deletions

View File

@@ -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",
],

View File

@@ -838,6 +838,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";

View File

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

View File

@@ -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.

View File

@@ -0,0 +1,188 @@
/*
* 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());
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.gsi.GsiProgress;
/** {@hide} */
interface IDynamicAndroidService
{
/**
* Start DynamicAndroid installation. This call may take 60~90 seconds. 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 true if the call succeeds
*/
boolean startInstallation(long systemSize, long userdataSize);
/**
* Query the progress of the current installation operation. This can be called while
* the installation is in progress.
*
* @return GsiProgress
*/
GsiProgress getInstallationProgress();
/**
* 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 true if the call succeeds
*/
boolean abort();
/**
* @return true if the device is running an DynamicAnroid image
*/
boolean isInUse();
/**
* @return true if the device has an DynamicAndroid image installed
*/
boolean isInstalled();
/**
* Remove DynamicAndroid installation if present
*
* @return true if the call succeeds
*/
boolean remove();
/**
* Enable DynamicAndroid when it's not enabled, otherwise, disable it.
*
* @return true if the call succeeds
*/
boolean toggle();
/**
* Write a chunk of the DynamicAndroid system image
*
* @return true if the call succeeds
*/
boolean write(in byte[] buf);
/**
* Finish write and make device to boot into the it after reboot.
*
* @return true if the call succeeds
*/
boolean commit();
}

View File

@@ -1681,6 +1681,10 @@
<permission android:name="android.permission.HARDWARE_TEST"
android:protectionLevel="signature" />
<!-- @hide Allows an application to manage DynamicAndroid image -->
<permission android:name="android.permission.MANAGE_DYNAMIC_ANDROID"
android:protectionLevel="signature" />
<!-- @SystemApi Allows access to Broadcast Radio
@hide This is not a third-party API (intended for system apps).-->
<permission android:name="android.permission.ACCESS_BROADCAST_RADIO"

View File

@@ -7,6 +7,7 @@ java_library_static {
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
"system/vold/binder",
"system/gsid/aidl",
],
},
srcs: [
@@ -15,6 +16,7 @@ java_library_static {
":installd_aidl",
":storaged_aidl",
":vold_aidl",
":gsiservice_aidl",
":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",

View File

@@ -0,0 +1,134 @@
/*
* 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.GsiProgress;
import android.gsi.IGsiService;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IDynamicAndroidService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
/**
* DynamicAndroidService implements IDynamicAndroidService. It provides permission check before
* passing requests to gsid
*/
public class DynamicAndroidService extends IDynamicAndroidService.Stub implements DeathRecipient {
private static final String TAG = "DynamicAndroidService";
private static final String NO_SERVICE_ERROR = "no gsiservice";
private Context mContext;
private volatile IGsiService mGsiService;
DynamicAndroidService(Context context) {
mContext = context;
}
private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
IBinder binder = ServiceManager.getService("gsiservice");
if (binder == null) {
throw new RemoteException(NO_SERVICE_ERROR);
}
/**
* 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();
synchronized (this) {
if (mGsiService == null) {
mGsiService = connect(this);
}
return mGsiService;
}
}
private void checkPermission() {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MANAGE_DYNAMIC_ANDROID permission");
}
}
@Override
public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
return getGsiService().startGsiInstall(systemSize, userdataSize, true) == 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 {
return getGsiService().isGsiRunning();
}
@Override
public boolean isInstalled() throws RemoteException {
return getGsiService().isGsiInstalled();
}
@Override
public boolean remove() throws RemoteException {
return getGsiService().removeGsiInstall();
}
@Override
public boolean toggle() throws RemoteException {
IGsiService gsiService = getGsiService();
if (gsiService.isGsiRunning()) {
return gsiService.disableGsiInstall();
} else {
return gsiService.setGsiBootable() == 0;
}
}
@Override
public boolean write(byte[] buf) throws RemoteException {
return getGsiService().commitGsiChunkFromMemory(buf);
}
@Override
public boolean commit() throws RemoteException {
return getGsiService().setGsiBootable() == 0;
}
}

View File

@@ -758,6 +758,7 @@ public final class SystemServer {
private void startOtherServices() {
final Context context = mSystemContext;
VibratorService vibrator = null;
DynamicAndroidService dynamicAndroid = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
@@ -867,6 +868,11 @@ public final class SystemServer {
ServiceManager.addService("vibrator", vibrator);
traceEnd();
traceBeginAndSlog("StartDynamicAndroidService");
dynamicAndroid = new DynamicAndroidService(context);
ServiceManager.addService("dynamic_android", dynamicAndroid);
traceEnd();
if (!isWatch) {
traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);

View File

@@ -0,0 +1,47 @@
/*
* 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.os.IDynamicAndroidService;
import android.os.ServiceManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
public class DynamicAndroidServiceTest extends AndroidTestCase {
private static final String TAG = "DynamicAndroidServiceTests";
private IDynamicAndroidService mService;
@Override
protected void setUp() throws Exception {
mService =
IDynamicAndroidService.Stub.asInterface(
ServiceManager.getService("dynamic_android"));
}
@LargeTest
public void test1() {
assertTrue("dynamic_android service available", mService != null);
try {
mService.startInstallation(1 << 20, 8 << 30);
fail("DynamicAndroidService did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
} catch (Exception e) {
fail(e.toString());
}
}
}