Merge "Add the DynamicAndroid Service"
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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));
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
core/java/android/os/DynamicAndroidManager.java
Normal file
188
core/java/android/os/DynamicAndroidManager.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
87
core/java/android/os/IDynamicAndroidService.aidl
Normal file
87
core/java/android/os/IDynamicAndroidService.aidl
Normal 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();
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
134
services/core/java/com/android/server/DynamicAndroidService.java
Normal file
134
services/core/java/com/android/server/DynamicAndroidService.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user