[Forensic] Add ForensicManager and permissions
The ForensicManager serves as client lib to communicate with the
ForensicService.
The following 3 permissions are introduced to guard the ForensicService:
READ_FORENSIC_STATE: Allows an application to monitor the state
of the ForensicService.
MANAGE_FORENSIC_STATE: Allows an application to change the state
of the ForensicService.
BIND_FORENSIC_BACKUP_SERVICE:Must be required by any
ForensicBackupService to ensure that only the system can bind to it.
Bug: 365994454
Test: atest ForensicServiceTest ForensicManagerTest
Flag: android.security.afl_api
Ignore-AOSP-First: security feature
Change-Id: Icc196812fd85da1c3f0b7860aab7b3c2e08e9046
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ed95fdd..b7d9fdd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -65,6 +65,7 @@
field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE";
+ field @FlaggedApi("android.security.afl_api") public static final String BIND_FORENSIC_EVENT_TRANSPORT_SERVICE = "android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE";
field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
field public static final String BIND_HOTWORD_DETECTION_SERVICE = "android.permission.BIND_HOTWORD_DETECTION_SERVICE";
field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
@@ -209,6 +210,7 @@
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field @FlaggedApi("android.security.afl_api") public static final String MANAGE_FORENSIC_STATE = "android.permission.MANAGE_FORENSIC_STATE";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
@@ -304,6 +306,7 @@
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
+ field @FlaggedApi("android.security.afl_api") public static final String READ_FORENSIC_STATE = "android.permission.READ_FORENSIC_STATE";
field public static final String READ_GLOBAL_APP_SEARCH_DATA = "android.permission.READ_GLOBAL_APP_SEARCH_DATA";
field @FlaggedApi("android.content.pm.get_resolved_apk_path") public static final String READ_INSTALLED_SESSION_PATHS = "android.permission.READ_INSTALLED_SESSION_PATHS";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
@@ -12570,6 +12573,29 @@
}
+package android.security.forensic {
+
+ @FlaggedApi("android.security.afl_api") public class ForensicManager {
+ method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void addStateCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void disable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void enable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void removeStateCallback(@NonNull java.util.function.Consumer<java.lang.Integer>);
+ field public static final int ERROR_DATA_SOURCE_UNAVAILABLE = 4; // 0x4
+ field public static final int ERROR_PERMISSION_DENIED = 1; // 0x1
+ field public static final int ERROR_TRANSPORT_UNAVAILABLE = 3; // 0x3
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ field public static final int STATE_DISABLED = 1; // 0x1
+ field public static final int STATE_ENABLED = 2; // 0x2
+ field public static final int STATE_UNKNOWN = 0; // 0x0
+ }
+
+ public static interface ForensicManager.CommandCallback {
+ method public void onFailure(int);
+ method public void onSuccess();
+ }
+
+}
+
package android.security.keystore {
public class AndroidKeyStoreProvider extends java.security.Provider {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e451116..f6ae8a9 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -238,6 +238,8 @@
import android.security.advancedprotection.IAdvancedProtectionService;
import android.security.attestationverification.AttestationVerificationManager;
import android.security.attestationverification.IAttestationVerificationManagerService;
+import android.security.forensic.ForensicManager;
+import android.security.forensic.IForensicService;
import android.security.keystore.KeyStoreManager;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
@@ -1790,6 +1792,18 @@
}
});
+ registerService(Context.FORENSIC_SERVICE, ForensicManager.class,
+ new CachedServiceFetcher<ForensicManager>() {
+ @Override
+ public ForensicManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.FORENSIC_SERVICE);
+ IForensicService service = IForensicService.Stub.asInterface(b);
+ return new ForensicManager(service);
+ }
+ });
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/security/forensic/ForensicManager.java b/core/java/android/security/forensic/ForensicManager.java
new file mode 100644
index 0000000..9126182
--- /dev/null
+++ b/core/java/android/security/forensic/ForensicManager.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2024 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.security.forensic;
+
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.security.Flags;
+import android.util.Log;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * ForensicManager manages the forensic logging on Android devices.
+ * Upon user consent, forensic logging collects various device events for
+ * off-device investigation of potential device compromise.
+ * <p>
+ * Forensic logging can either be enabled ({@link #STATE_ENABLED}
+ * or disabled ({@link #STATE_DISABLED}).
+ * <p>
+ * The Forensic logs will be transferred to
+ * {@link android.security.forensic.ForensicEventTransport}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_AFL_API)
+@SystemService(Context.FORENSIC_SERVICE)
+public class ForensicManager {
+ private static final String TAG = "ForensicManager";
+
+ /** @hide */
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_UNKNOWN,
+ STATE_DISABLED,
+ STATE_ENABLED
+ })
+ public @interface ForensicState {}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN,
+ ERROR_PERMISSION_DENIED,
+ ERROR_TRANSPORT_UNAVAILABLE,
+ ERROR_DATA_SOURCE_UNAVAILABLE
+ })
+ public @interface ForensicError {}
+
+ /**
+ * Indicates an unknown state
+ */
+ public static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+
+ /**
+ * Indicates an state that the forensic is turned off.
+ */
+ public static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
+
+ /**
+ * Indicates an state that the forensic is turned on.
+ */
+ public static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+ /**
+ * Indicates an unknown error
+ */
+ public static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+
+ /**
+ * Indicates an error due to insufficient access rights.
+ */
+ public static final int ERROR_PERMISSION_DENIED =
+ IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+
+ /**
+ * Indicates an error due to unavailability of the forensic event transport.
+ */
+ public static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
+
+ /**
+ * Indicates an error due to unavailability of the data source.
+ */
+ public static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+
+ private final IForensicService mService;
+
+ private final ConcurrentHashMap<Consumer<Integer>, IForensicServiceStateCallback>
+ mStateCallbacks = new ConcurrentHashMap<>();
+
+ /**
+ * Constructor
+ *
+ * @param service A valid instance of IForensicService.
+ * @hide
+ */
+ public ForensicManager(IForensicService service) {
+ mService = service;
+ }
+
+ /**
+ * Add a callback to monitor the state of the ForensicService.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for state change.
+ * Once the callback is registered, the callback will be called
+ * to reflect the init state.
+ * The callback can be registered only once.
+ */
+ @RequiresPermission(READ_FORENSIC_STATE)
+ public void addStateCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull @ForensicState Consumer<Integer> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ if (mStateCallbacks.get(callback) != null) {
+ Log.d(TAG, "addStateCallback callback already present");
+ return;
+ }
+
+ final IForensicServiceStateCallback wrappedCallback =
+ new IForensicServiceStateCallback.Stub() {
+ @Override
+ public void onStateChange(int state) {
+ executor.execute(() -> callback.accept(state));
+ }
+ };
+ try {
+ mService.addStateCallback(wrappedCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ mStateCallbacks.put(callback, wrappedCallback);
+ }
+
+ /**
+ * Remove a callback to monitor the state of the ForensicService.
+ *
+ * @param callback The callback to remove.
+ */
+ @RequiresPermission(READ_FORENSIC_STATE)
+ public void removeStateCallback(@NonNull Consumer<@ForensicState Integer> callback) {
+ Objects.requireNonNull(callback);
+ if (!mStateCallbacks.containsKey(callback)) {
+ Log.d(TAG, "removeStateCallback callback not present");
+ return;
+ }
+
+ IForensicServiceStateCallback wrappedCallback = mStateCallbacks.get(callback);
+
+ try {
+ mService.removeStateCallback(wrappedCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ mStateCallbacks.remove(callback);
+ }
+
+ /**
+ * Enable forensic logging.
+ * If successful, ForensicService will transition to {@link #STATE_ENABLED} state.
+ * <p>
+ * When forensic logging is enabled, various device events will be collected and
+ * sent over to the registered {@link android.security.forensic.ForensicEventTransport}.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for the command result.
+ */
+ @RequiresPermission(MANAGE_FORENSIC_STATE)
+ public void enable(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CommandCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ try {
+ mService.enable(new IForensicServiceCommandCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ executor.execute(callback::onSuccess);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ executor.execute(() -> callback.onFailure(error));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disable forensic logging.
+ * If successful, ForensicService will transition to {@link #STATE_DISABLED}.
+ * <p>
+ * When forensic logging is disabled, device events will no longer be collected.
+ * Any events that have been collected but not yet sent to ForensicEventTransport
+ * will be transferred as a final batch.
+ *
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback for the command result.
+ */
+ @RequiresPermission(MANAGE_FORENSIC_STATE)
+ public void disable(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CommandCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ try {
+ mService.disable(new IForensicServiceCommandCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ executor.execute(callback::onSuccess);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ executor.execute(() -> callback.onFailure(error));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callback used in {@link #enable} and {@link #disable} to indicate the result of the command.
+ */
+ public interface CommandCallback {
+ /**
+ * Called when command succeeds.
+ */
+ void onSuccess();
+
+ /**
+ * Called when command fails.
+ * @param error The error number.
+ */
+ void onFailure(@ForensicError int error);
+ }
+}
diff --git a/core/java/android/security/forensic/IBackupTransport.aidl b/core/java/android/security/forensic/IForensicEventTransport.aidl
similarity index 96%
rename from core/java/android/security/forensic/IBackupTransport.aidl
rename to core/java/android/security/forensic/IForensicEventTransport.aidl
index c2cbc83..80e78eb 100644
--- a/core/java/android/security/forensic/IBackupTransport.aidl
+++ b/core/java/android/security/forensic/IForensicEventTransport.aidl
@@ -20,7 +20,7 @@
import com.android.internal.infra.AndroidFuture;
/** {@hide} */
-oneway interface IBackupTransport {
+oneway interface IForensicEventTransport {
/**
* Initialize the server side.
*/
diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl
index a944b18..8039b26 100644
--- a/core/java/android/security/forensic/IForensicService.aidl
+++ b/core/java/android/security/forensic/IForensicService.aidl
@@ -24,9 +24,12 @@
* @hide
*/
interface IForensicService {
- void monitorState(IForensicServiceStateCallback callback);
- void makeVisible(IForensicServiceCommandCallback callback);
- void makeInvisible(IForensicServiceCommandCallback callback);
+ @EnforcePermission("READ_FORENSIC_STATE")
+ void addStateCallback(IForensicServiceStateCallback callback);
+ @EnforcePermission("READ_FORENSIC_STATE")
+ void removeStateCallback(IForensicServiceStateCallback callback);
+ @EnforcePermission("MANAGE_FORENSIC_STATE")
void enable(IForensicServiceCommandCallback callback);
+ @EnforcePermission("MANAGE_FORENSIC_STATE")
void disable(IForensicServiceCommandCallback callback);
}
diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
index 7fa0c7f..6d1456e 100644
--- a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
+++ b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
@@ -25,8 +25,8 @@
UNKNOWN = 0,
PERMISSION_DENIED = 1,
INVALID_STATE_TRANSITION = 2,
- BACKUP_TRANSPORT_UNAVAILABLE = 3,
- DATA_SOURCE_UNAVAILABLE = 3,
+ TRANSPORT_UNAVAILABLE = 3,
+ DATA_SOURCE_UNAVAILABLE = 4,
}
void onSuccess();
void onFailure(ErrorCode error);
diff --git a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
index 0cda350..1b68c7b 100644
--- a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
+++ b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
@@ -23,9 +23,8 @@
@Backing(type="int")
enum State{
UNKNOWN = 0,
- INVISIBLE = 1,
- VISIBLE = 2,
- ENABLED = 3,
+ DISABLED = 1,
+ ENABLED = 2,
}
void onStateChange(State state);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3e0c120..eb26331 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4144,6 +4144,37 @@
<uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
android:featureFlag="android.security.aapm_api"/>
+ <!-- Allows an application to read the state of the ForensicService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Allows an application to change the state of the ForensicService
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+ <!-- Must be required by any ForensicEventTransportService to ensure that
+ only the system can bind to it.
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE"
+ android:featureFlag="android.security.afl_api"/>
+
<!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
<permission android:name="android.permission.PROVISION_DEMO_DEVICE"
android:protectionLevel="signature|setup|knownSigner"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 969ee2e..3287725 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7212,8 +7212,8 @@
<!-- Package for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] -->
<string name="identity_check_settings_package_name">com\u002eandroid\u002esettings</string>
- <!-- The name of the service for forensic backup transport. -->
- <string name="config_forensicBackupTransport" translatable="false"></string>
+ <!-- The name of the service for forensic event transport. -->
+ <string name="config_forensicEventTransport" translatable="false"></string>
<!-- Whether to enable fp unlock when screen turns off on udfps devices -->
<bool name="config_screen_off_udfps_enabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9dd3027..7fe0912 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5679,8 +5679,8 @@
<java-symbol type="string" name="identity_check_settings_action" />
<java-symbol type="string" name="identity_check_settings_package_name" />
- <!-- Forensic backup transport -->
- <java-symbol type="string" name="config_forensicBackupTransport" />
+ <!-- Forensic event transport -->
+ <java-symbol type="string" name="config_forensicEventTransport" />
<!-- Fingerprint screen off unlock config -->
<java-symbol type="bool" name="config_screen_off_udfps_enabled" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 541ca60..7423567 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -597,6 +597,9 @@
<!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest -->
<permission name="android.permission.READ_SYSTEM_PREFERENCES" />
<permission name="android.permission.WRITE_SYSTEM_PREFERENCES" />
+ <!-- Permission required for CTS test - ForensicManagerTest -->
+ <permission name="android.permission.READ_FORENSIC_STATE" />
+ <permission name="android.permission.MANAGE_FORENSIC_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7b6321d..859445e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -953,6 +953,13 @@
<uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
android:featureFlag="android.security.aapm_api"/>
+ <!-- Permission required for CTS test - ForensicManagerTest -->
+ <uses-permission android:name="android.permission.READ_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+ <uses-permission android:name="android.permission.MANAGE_FORENSIC_STATE"
+ android:featureFlag="android.security.afl_api"/>
+
+
<!-- Permission required for CTS test - CtsAppTestCases -->
<uses-permission android:name="android.permission.KILL_UID" />
diff --git a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java b/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
similarity index 69%
rename from services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
rename to services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
index caca011..b85199e 100644
--- a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java
@@ -16,15 +16,19 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.forensic.ForensicEvent;
-import android.security.forensic.IBackupTransport;
+import android.security.forensic.IForensicEventTransport;
import android.text.TextUtils;
import android.util.Slog;
@@ -36,20 +40,20 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-public class BackupTransportConnection implements ServiceConnection {
- private static final String TAG = "BackupTransportConnection";
+public class ForensicEventTransportConnection implements ServiceConnection {
+ private static final String TAG = "ForensicEventTransportConnection";
private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins
private final Context mContext;
- private String mForensicBackupTransportConfig;
- volatile IBackupTransport mService;
+ private String mForensicEventTransportConfig;
+ volatile IForensicEventTransport mService;
- public BackupTransportConnection(Context context) {
+ public ForensicEventTransportConnection(Context context) {
mContext = context;
mService = null;
}
/**
- * Initialize the BackupTransport binder service.
+ * Initialize the ForensicEventTransport binder service.
* @return Whether the initialization succeed.
*/
public boolean initialize() {
@@ -74,7 +78,7 @@
}
/**
- * Add data to the BackupTransport binder service.
+ * Add data to the ForensicEventTransport binder service.
* @param data List of ForensicEvent.
* @return Whether the data is added to the binder service.
*/
@@ -109,21 +113,37 @@
return future.get(FUTURE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException
| CancellationException e) {
- Slog.w(TAG, "Failed to get result from transport:", e);
+ Slog.e(TAG, "Failed to get result from transport:", e);
return null;
}
}
private boolean bindService() {
- mForensicBackupTransportConfig = mContext.getString(
- com.android.internal.R.string.config_forensicBackupTransport);
- if (TextUtils.isEmpty(mForensicBackupTransportConfig)) {
+ mForensicEventTransportConfig = mContext.getString(
+ com.android.internal.R.string.config_forensicEventTransport);
+ if (TextUtils.isEmpty(mForensicEventTransportConfig)) {
+ Slog.e(TAG, "config_forensicEventTransport is empty");
return false;
}
ComponentName serviceComponent =
- ComponentName.unflattenFromString(mForensicBackupTransportConfig);
+ ComponentName.unflattenFromString(mForensicEventTransportConfig);
if (serviceComponent == null) {
+ Slog.e(TAG, "Can't get serviceComponent name");
+ return false;
+ }
+
+ try {
+ ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(serviceComponent,
+ 0 /* flags */);
+ if (!BIND_FORENSIC_EVENT_TRANSPORT_SERVICE.equals(serviceInfo.permission)) {
+ Slog.e(TAG, serviceComponent.flattenToShortString()
+ + " is not declared with the permission "
+ + "\"" + BIND_FORENSIC_EVENT_TRANSPORT_SERVICE + "\"");
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Unable to find serviceComponent");
return false;
}
@@ -143,7 +163,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IBackupTransport.Stub.asInterface(service);
+ mService = IForensicEventTransport.Stub.asInterface(service);
}
@Override
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 01f630b..2be068f 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -16,11 +16,16 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicService;
@@ -41,16 +46,15 @@
public class ForensicService extends SystemService {
private static final String TAG = "ForensicService";
- private static final int MSG_MONITOR_STATE = 0;
- private static final int MSG_MAKE_VISIBLE = 1;
- private static final int MSG_MAKE_INVISIBLE = 2;
- private static final int MSG_ENABLE = 3;
- private static final int MSG_DISABLE = 4;
- private static final int MSG_BACKUP = 5;
+ private static final int MAX_STATE_CALLBACK_NUM = 16;
+ private static final int MSG_ADD_STATE_CALLBACK = 0;
+ private static final int MSG_REMOVE_STATE_CALLBACK = 1;
+ private static final int MSG_ENABLE = 2;
+ private static final int MSG_DISABLE = 3;
+ private static final int MSG_TRANSPORT = 4;
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
- private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
- private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
@@ -58,19 +62,19 @@
IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
private static final int ERROR_INVALID_STATE_TRANSITION =
IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
- private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
- IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
private final Context mContext;
private final Handler mHandler;
- private final BackupTransportConnection mBackupTransportConnection;
+ private final ForensicEventTransportConnection mForensicEventTransportConnection;
private final DataAggregator mDataAggregator;
private final BinderService mBinderService;
- private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
- private volatile int mState = STATE_INVISIBLE;
+ private final ArrayList<IForensicServiceStateCallback> mStateCallbacks = new ArrayList<>();
+ private volatile int mState = STATE_DISABLED;
public ForensicService(@NonNull Context context) {
this(new InjectorImpl(context));
@@ -81,9 +85,9 @@
super(injector.getContext());
mContext = injector.getContext();
mHandler = new EventHandler(injector.getLooper(), this);
- mBackupTransportConnection = injector.getBackupTransportConnection();
+ mForensicEventTransportConnection = injector.getForensicEventransportConnection();
mDataAggregator = injector.getDataAggregator(this);
- mBinderService = new BinderService(this);
+ mBinderService = new BinderService(this, injector.getPermissionEnforcer());
}
@VisibleForTesting
@@ -94,32 +98,36 @@
private static final class BinderService extends IForensicService.Stub {
final ForensicService mService;
- BinderService(ForensicService service) {
+ BinderService(ForensicService service, @NonNull PermissionEnforcer permissionEnforcer) {
+ super(permissionEnforcer);
mService = service;
}
@Override
- public void monitorState(IForensicServiceStateCallback callback) {
- mService.mHandler.obtainMessage(MSG_MONITOR_STATE, callback).sendToTarget();
+ @EnforcePermission(READ_FORENSIC_STATE)
+ public void addStateCallback(IForensicServiceStateCallback callback) {
+ addStateCallback_enforcePermission();
+ mService.mHandler.obtainMessage(MSG_ADD_STATE_CALLBACK, callback).sendToTarget();
}
@Override
- public void makeVisible(IForensicServiceCommandCallback callback) {
- mService.mHandler.obtainMessage(MSG_MAKE_VISIBLE, callback).sendToTarget();
+ @EnforcePermission(READ_FORENSIC_STATE)
+ public void removeStateCallback(IForensicServiceStateCallback callback) {
+ removeStateCallback_enforcePermission();
+ mService.mHandler.obtainMessage(MSG_REMOVE_STATE_CALLBACK, callback).sendToTarget();
}
@Override
- public void makeInvisible(IForensicServiceCommandCallback callback) {
- mService.mHandler.obtainMessage(MSG_MAKE_INVISIBLE, callback).sendToTarget();
- }
-
- @Override
+ @EnforcePermission(MANAGE_FORENSIC_STATE)
public void enable(IForensicServiceCommandCallback callback) {
+ enable_enforcePermission();
mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget();
}
@Override
+ @EnforcePermission(MANAGE_FORENSIC_STATE)
public void disable(IForensicServiceCommandCallback callback) {
+ disable_enforcePermission();
mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget();
}
}
@@ -135,24 +143,18 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_MONITOR_STATE:
+ case MSG_ADD_STATE_CALLBACK:
try {
- mService.monitorState(
+ mService.addStateCallback(
(IForensicServiceStateCallback) msg.obj);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
break;
- case MSG_MAKE_VISIBLE:
+ case MSG_REMOVE_STATE_CALLBACK:
try {
- mService.makeVisible((IForensicServiceCommandCallback) msg.obj);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- break;
- case MSG_MAKE_INVISIBLE:
- try {
- mService.makeInvisible((IForensicServiceCommandCallback) msg.obj);
+ mService.removeStateCallback(
+ (IForensicServiceStateCallback) msg.obj);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
@@ -171,8 +173,8 @@
Slog.e(TAG, "RemoteException", e);
}
break;
- case MSG_BACKUP:
- mService.backup((List<ForensicEvent>) msg.obj);
+ case MSG_TRANSPORT:
+ mService.transport((List<ForensicEvent>) msg.obj);
break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
@@ -180,103 +182,83 @@
}
}
- private void monitorState(IForensicServiceStateCallback callback) throws RemoteException {
- for (int i = 0; i < mStateMonitors.size(); i++) {
- if (mStateMonitors.get(i).asBinder() == callback.asBinder()) {
+ private void addStateCallback(IForensicServiceStateCallback callback) throws RemoteException {
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) {
return;
}
}
- mStateMonitors.add(callback);
+ mStateCallbacks.add(callback);
callback.onStateChange(mState);
}
- private void notifyStateMonitors() throws RemoteException {
- for (int i = 0; i < mStateMonitors.size(); i++) {
- mStateMonitors.get(i).onStateChange(mState);
+ private void removeStateCallback(IForensicServiceStateCallback callback)
+ throws RemoteException {
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) {
+ mStateCallbacks.remove(i);
+ return;
+ }
}
}
- private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_INVISIBLE:
- if (!mDataAggregator.initialize()) {
- callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
- break;
- }
- mState = STATE_VISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_VISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ private void notifyStateMonitors() {
+ if (mStateCallbacks.size() >= MAX_STATE_CALLBACK_NUM) {
+ mStateCallbacks.removeFirst();
}
- }
- private void makeInvisible(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_VISIBLE:
- case STATE_ENABLED:
- mState = STATE_INVISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_INVISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ for (int i = 0; i < mStateCallbacks.size(); i++) {
+ try {
+ mStateCallbacks.get(i).onStateChange(mState);
+ } catch (RemoteException e) {
+ mStateCallbacks.remove(i);
+ }
}
}
private void enable(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_VISIBLE:
- if (!mBackupTransportConnection.initialize()) {
- callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
- break;
- }
- mDataAggregator.enable();
- mState = STATE_ENABLED;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_ENABLED:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ if (mState == STATE_ENABLED) {
+ callback.onSuccess();
+ return;
}
+
+ // TODO: temporarily disable the following for the CTS ForensicManagerTest.
+ // Enable it when the transport component is ready.
+ // if (!mForensicEventTransportConnection.initialize()) {
+ // callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE);
+ // return;
+ // }
+
+ mDataAggregator.enable();
+ mState = STATE_ENABLED;
+ notifyStateMonitors();
+ callback.onSuccess();
}
private void disable(IForensicServiceCommandCallback callback) throws RemoteException {
- switch (mState) {
- case STATE_ENABLED:
- mBackupTransportConnection.release();
- mDataAggregator.disable();
- mState = STATE_VISIBLE;
- notifyStateMonitors();
- callback.onSuccess();
- break;
- case STATE_VISIBLE:
- callback.onSuccess();
- break;
- default:
- callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ if (mState == STATE_DISABLED) {
+ callback.onSuccess();
+ return;
}
+
+ // TODO: temporarily disable the following for the CTS ForensicManagerTest.
+ // Enable it when the transport component is ready.
+ // mForensicEventTransportConnection.release();
+ mDataAggregator.disable();
+ mState = STATE_DISABLED;
+ notifyStateMonitors();
+ callback.onSuccess();
}
/**
* Add a list of ForensicEvent.
*/
public void addNewData(List<ForensicEvent> events) {
- mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+ mHandler.obtainMessage(MSG_TRANSPORT, events).sendToTarget();
}
- private void backup(List<ForensicEvent> events) {
- mBackupTransportConnection.addData(events);
+ private void transport(List<ForensicEvent> events) {
+ mForensicEventTransportConnection.addData(events);
}
@Override
@@ -296,9 +278,11 @@
interface Injector {
Context getContext();
+ PermissionEnforcer getPermissionEnforcer();
+
Looper getLooper();
- BackupTransportConnection getBackupTransportConnection();
+ ForensicEventTransportConnection getForensicEventransportConnection();
DataAggregator getDataAggregator(ForensicService forensicService);
}
@@ -315,6 +299,10 @@
return mContext;
}
+ @Override
+ public PermissionEnforcer getPermissionEnforcer() {
+ return PermissionEnforcer.fromContext(mContext);
+ }
@Override
public Looper getLooper() {
@@ -326,8 +314,8 @@
}
@Override
- public BackupTransportConnection getBackupTransportConnection() {
- return new BackupTransportConnection(mContext);
+ public ForensicEventTransportConnection getForensicEventransportConnection() {
+ return new ForensicEventTransportConnection(mContext);
}
@Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d0bf02d..3e7c4ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -251,6 +251,7 @@
import com.android.server.security.KeyChainSystemService;
import com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService;
import com.android.server.security.advancedprotection.AdvancedProtectionService;
+import com.android.server.security.forensic.ForensicService;
import com.android.server.security.rkp.RemoteProvisioningService;
import com.android.server.selinux.SelinuxAuditLogsService;
import com.android.server.sensorprivacy.SensorPrivacyService;
@@ -1760,6 +1761,13 @@
mSystemServiceManager.startService(LogcatManagerService.class);
t.traceEnd();
+ if (!isWatch && !isTv && !isAutomotive
+ && android.security.Flags.aflApi()) {
+ t.traceBegin("StartForensicService");
+ mSystemServiceManager.startService(ForensicService.class);
+ t.traceEnd();
+ }
+
if (AppFunctionManagerConfiguration.isSupported(context)) {
t.traceBegin("StartAppFunctionManager");
mSystemServiceManager.startService(AppFunctionManagerService.class);
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 40e0034..0da6db6 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -16,9 +16,13 @@
package com.android.server.security.forensic;
+import static android.Manifest.permission.MANAGE_FORENSIC_STATE;
+import static android.Manifest.permission.READ_FORENSIC_STATE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -29,7 +33,9 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Looper;
+import android.os.PermissionEnforcer;
import android.os.RemoteException;
+import android.os.test.FakePermissionEnforcer;
import android.os.test.TestLooper;
import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicServiceCommandCallback;
@@ -41,6 +47,7 @@
import com.android.server.ServiceThread;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -50,34 +57,36 @@
public class ForensicServiceTest {
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
- private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
- private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED;
private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
private static final int ERROR_PERMISSION_DENIED =
IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
- private static final int ERROR_INVALID_STATE_TRANSITION =
- IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
- private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
- IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE;
private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
private Context mContext;
- private BackupTransportConnection mBackupTransportConnection;
+ private ForensicEventTransportConnection mForensicEventTransportConnection;
private DataAggregator mDataAggregator;
private ForensicService mForensicService;
private TestLooper mTestLooper;
private Looper mLooper;
private TestLooper mTestLooperOfDataAggregator;
private Looper mLooperOfDataAggregator;
+ private FakePermissionEnforcer mPermissionEnforcer;
@SuppressLint("VisibleForTests")
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
+ mPermissionEnforcer = new FakePermissionEnforcer();
+ mPermissionEnforcer.grant(READ_FORENSIC_STATE);
+ mPermissionEnforcer.grant(MANAGE_FORENSIC_STATE);
+
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
mTestLooperOfDataAggregator = new TestLooper();
@@ -87,217 +96,101 @@
}
@Test
- public void testMonitorState_Invisible() throws RemoteException {
+ public void testAddStateCallback_NoPermission() {
+ mPermissionEnforcer.revoke(READ_FORENSIC_STATE);
StateCallback scb = new StateCallback();
assertEquals(STATE_UNKNOWN, scb.mState);
- mForensicService.getBinderService().monitorState(scb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb.mState);
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().addStateCallback(scb));
}
@Test
- public void testMonitorState_Invisible_TwoMonitors() throws RemoteException {
+ public void testRemoveStateCallback_NoPermission() {
+ mPermissionEnforcer.revoke(READ_FORENSIC_STATE);
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().removeStateCallback(scb));
+ }
+
+ @Test
+ public void testEnable_NoPermission() {
+ mPermissionEnforcer.revoke(MANAGE_FORENSIC_STATE);
+
+ CommandCallback ccb = new CommandCallback();
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().enable(ccb));
+ }
+
+ @Test
+ public void testDisable_NoPermission() {
+ mPermissionEnforcer.revoke(MANAGE_FORENSIC_STATE);
+
+ CommandCallback ccb = new CommandCallback();
+ assertThrows(SecurityException.class,
+ () -> mForensicService.getBinderService().disable(ccb));
+ }
+
+ @Test
+ public void testAddStateCallback_Disabled() throws RemoteException {
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ mForensicService.getBinderService().addStateCallback(scb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb.mState);
+ }
+
+ @Test
+ public void testAddStateCallback_Disabled_TwoStateCallbacks() throws RemoteException {
StateCallback scb1 = new StateCallback();
assertEquals(STATE_UNKNOWN, scb1.mState);
- mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().addStateCallback(scb1);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
StateCallback scb2 = new StateCallback();
assertEquals(STATE_UNKNOWN, scb2.mState);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
}
@Test
- public void testMakeVisible_FromInvisible() throws RemoteException {
- StateCallback scb = new StateCallback();
- assertEquals(STATE_UNKNOWN, scb.mState);
- mForensicService.getBinderService().monitorState(scb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
+ public void testRemoveStateCallback() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
doReturn(true).when(mDataAggregator).initialize();
+ doReturn(true).when(mForensicEventTransportConnection).initialize();
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
- throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
-
- doReturn(false).when(mDataAggregator).initialize();
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeVisible_FromEnabled_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_ENABLED);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeVisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testMakeInvisible_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeInvisible_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
- @Test
- public void testMakeInvisible_FromEnabled_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_ENABLED);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_ENABLED, scb1.mState);
- assertEquals(STATE_ENABLED, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().makeInvisible(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNull(ccb.mErrorCode);
- }
-
-
- @Test
- public void testEnable_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ mForensicService.getBinderService().removeStateCallback(scb2);
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+ assertNull(ccb.mErrorCode);
}
@Test
- public void testEnable_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
+ public void testEnable_FromDisabled_TwoStateCallbacks() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
- doReturn(true).when(mBackupTransportConnection).initialize();
+ doReturn(true).when(mForensicEventTransportConnection).initialize();
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
@@ -310,35 +203,13 @@
}
@Test
- public void testEnable_FromVisible_TwoMonitors_BackupTransportUnavailable()
+ public void testEnable_FromEnabled_TwoStateCallbacks()
throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- doReturn(false).when(mBackupTransportConnection).initialize();
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().enable(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_BACKUP_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue());
- }
-
- @Test
- public void testEnable_FromEnabled_TwoMonitors() throws RemoteException {
mForensicService.setState(STATE_ENABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
@@ -346,62 +217,44 @@
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
+
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
@Test
- public void testDisable_FromInvisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_INVISIBLE);
+ public void testDisable_FromDisabled_TwoStateCallbacks() throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().disable(ccb);
mTestLooper.dispatchAll();
- assertEquals(STATE_INVISIBLE, scb1.mState);
- assertEquals(STATE_INVISIBLE, scb2.mState);
- assertNotNull(ccb.mErrorCode);
- assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
- }
- @Test
- public void testDisable_FromVisible_TwoMonitors() throws RemoteException {
- mForensicService.setState(STATE_VISIBLE);
- StateCallback scb1 = new StateCallback();
- StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
-
- CommandCallback ccb = new CommandCallback();
- mForensicService.getBinderService().disable(ccb);
- mTestLooper.dispatchAll();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
@Test
- public void testDisable_FromEnabled_TwoMonitors() throws RemoteException {
+ public void testDisable_FromEnabled_TwoStateCallbacks() throws RemoteException {
mForensicService.setState(STATE_ENABLED);
StateCallback scb1 = new StateCallback();
StateCallback scb2 = new StateCallback();
- mForensicService.getBinderService().monitorState(scb1);
- mForensicService.getBinderService().monitorState(scb2);
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
mTestLooper.dispatchAll();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
- doNothing().when(mBackupTransportConnection).release();
+ doNothing().when(mForensicEventTransportConnection).release();
ServiceThread mockThread = spy(ServiceThread.class);
mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
@@ -412,11 +265,35 @@
mTestLooperOfDataAggregator.dispatchAll();
// TODO: We can verify the data sources once we implement them.
verify(mockThread, times(1)).quitSafely();
- assertEquals(STATE_VISIBLE, scb1.mState);
- assertEquals(STATE_VISIBLE, scb2.mState);
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
+ @Ignore("Enable once the ForensicEventTransportConnection is ready")
+ @Test
+ public void testEnable_FromDisable_TwoStateCallbacks_TransportUnavailable()
+ throws RemoteException {
+ mForensicService.setState(STATE_DISABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().addStateCallback(scb1);
+ mForensicService.getBinderService().addStateCallback(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+
+ doReturn(false).when(mForensicEventTransportConnection).initialize();
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().enable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_DISABLED, scb1.mState);
+ assertEquals(STATE_DISABLED, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue());
+ }
+
@Test
public void testDataAggregator_AddBatchData() {
mForensicService.setState(STATE_ENABLED);
@@ -441,14 +318,14 @@
events.add(eventOne);
events.add(eventTwo);
- doReturn(true).when(mBackupTransportConnection).addData(any());
+ doReturn(true).when(mForensicEventTransportConnection).addData(any());
mDataAggregator.addBatchData(events);
mTestLooperOfDataAggregator.dispatchAll();
mTestLooper.dispatchAll();
ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
- verify(mBackupTransportConnection).addData(captor.capture());
+ verify(mForensicEventTransportConnection).addData(captor.capture());
List<ForensicEvent> receivedEvents = captor.getValue();
assertEquals(receivedEvents.size(), 2);
@@ -476,6 +353,10 @@
return mContext;
}
+ @Override
+ public PermissionEnforcer getPermissionEnforcer() {
+ return mPermissionEnforcer;
+ }
@Override
public Looper getLooper() {
@@ -483,9 +364,9 @@
}
@Override
- public BackupTransportConnection getBackupTransportConnection() {
- mBackupTransportConnection = spy(new BackupTransportConnection(mContext));
- return mBackupTransportConnection;
+ public ForensicEventTransportConnection getForensicEventransportConnection() {
+ mForensicEventTransportConnection = spy(new ForensicEventTransportConnection(mContext));
+ return mForensicEventTransportConnection;
}
@Override