Merge "New Pipeline: resume spinning after RemoteInputView is moved"
diff --git a/core/api/current.txt b/core/api/current.txt
index 19830c3..e2325b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7306,10 +7306,10 @@
method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
method public CharSequence getDeviceOwnerLockScreenInfo();
- method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
method @NonNull public String getEnrollmentSpecificId();
method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -23299,7 +23299,7 @@
ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
method @NonNull public android.media.RouteDiscoveryPreference build();
method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>);
- method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@Nullable java.util.List<java.lang.String>);
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setRequiredFeatures(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
@@ -31738,9 +31738,14 @@
method public void release();
method public void release(int);
method public void setReferenceCounted(boolean);
+ method public void setStateListener(@NonNull java.util.concurrent.Executor, @Nullable android.os.PowerManager.WakeLockStateListener);
method public void setWorkSource(android.os.WorkSource);
}
+ public static interface PowerManager.WakeLockStateListener {
+ method public void onStateChanged(boolean);
+ }
+
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ec39e9f..b3dd7a7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -203,6 +203,7 @@
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
+ field public static final String MODIFY_TOUCH_MODE_STATE = "android.permission.MODIFY_TOUCH_MODE_STATE";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -260,6 +261,7 @@
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
+ field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP";
field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -351,6 +353,7 @@
field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
+ field public static final String WRITE_SMS = "android.permission.WRITE_SMS";
}
public static final class Manifest.permission_group {
@@ -1060,8 +1063,8 @@
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
- method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
- method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
+ method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
+ method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1197,6 +1200,7 @@
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
method public boolean canDeviceOwnerGrantSensorsPermissions();
method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getAdminExtras();
method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
method public long getLocalTime();
method @Nullable public java.util.Locale getLocale();
@@ -1210,6 +1214,7 @@
public static final class FullyManagedDeviceProvisioningParams.Builder {
ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
@@ -1220,6 +1225,7 @@
public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.accounts.Account getAccountToMigrate();
+ method @NonNull public android.os.PersistableBundle getAdminExtras();
method @NonNull public String getOwnerName();
method @NonNull public android.content.ComponentName getProfileAdminComponentName();
method @Nullable public String getProfileName();
@@ -1234,6 +1240,7 @@
ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
@@ -2685,6 +2692,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[], @Nullable android.app.BroadcastOptions);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
@@ -2788,6 +2796,7 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+ field public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
@@ -2810,7 +2819,9 @@
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
field public static final String EXTRA_SHOWING_ATTRIBUTION = "android.intent.extra.SHOWING_ATTRIBUTION";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
+ field public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
+ field public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 16777216; // 0x1000000
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
}
@@ -5886,6 +5897,7 @@
field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
field public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
+ field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb
field public static final int STREAM_BLUETOOTH_SCO = 6; // 0x6
field public static final int SUCCESS = 0; // 0x0
@@ -10232,6 +10244,7 @@
method @Deprecated public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, boolean);
method public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, @Nullable String, boolean);
field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
+ field public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS";
field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS";
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
@@ -11047,7 +11060,7 @@
public class GameService extends android.app.Service {
ctor public GameService();
- method public final void createGameSession(@IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void createGameSession(@IntRange(from=0) int);
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
method public void onConnected();
method public void onDisconnected();
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 8234f03..5649c5f 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -128,6 +128,7 @@
"android/os/IThermalStatusListener.aidl",
"android/os/IThermalService.aidl",
"android/os/IPowerManager.aidl",
+ "android/os/IWakeLockCallback.aidl",
],
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 876e401..3289304 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1005,6 +1005,11 @@
RemoteCallback finishCallback;
}
+ static final class DumpResourcesData {
+ public ParcelFileDescriptor fd;
+ public RemoteCallback finishCallback;
+ }
+
static final class UpdateCompatibilityData {
String pkg;
CompatibilityInfo info;
@@ -1316,6 +1321,20 @@
sendMessage(H.SCHEDULE_CRASH, args, typeId);
}
+ @Override
+ public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) {
+ DumpResourcesData data = new DumpResourcesData();
+ try {
+ data.fd = fd.dup();
+ data.finishCallback = callback;
+ sendMessage(H.DUMP_RESOURCES, data, 0, 0, false /*async*/);
+ } catch (IOException e) {
+ Slog.w(TAG, "dumpResources failed", e);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
String prefix, String[] args) {
DumpComponentInfo data = new DumpComponentInfo();
@@ -2039,6 +2058,7 @@
public static final int UPDATE_UI_TRANSLATION_STATE = 163;
public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164;
public static final int DUMP_GFXINFO = 165;
+ public static final int DUMP_RESOURCES = 166;
public static final int INSTRUMENT_WITHOUT_RESTART = 170;
public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -2092,6 +2112,7 @@
case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
+ case DUMP_RESOURCES: return "DUMP_RESOURCES";
}
}
return Integer.toString(code);
@@ -2207,6 +2228,9 @@
case DUMP_HEAP:
handleDumpHeap((DumpHeapData) msg.obj);
break;
+ case DUMP_RESOURCES:
+ handleDumpResources((DumpResourcesData) msg.obj);
+ break;
case DUMP_ACTIVITY:
handleDumpActivity((DumpComponentInfo)msg.obj);
break;
@@ -4585,6 +4609,23 @@
}
}
+ private void handleDumpResources(DumpResourcesData info) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(
+ info.fd.getFileDescriptor()));
+
+ Resources.dumpHistory(pw, "");
+ pw.flush();
+ if (info.finishCallback != null) {
+ info.finishCallback.sendResult(null);
+ }
+ } finally {
+ IoUtils.closeQuietly(info.fd);
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
private void handleDumpActivity(DumpComponentInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f3315a8..8181a74 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1997,7 +1997,8 @@
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
- // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
+ // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and
+ // ActivityManagerService.LocalService.startAndBindSupplementalProcessService
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
@@ -2023,10 +2024,10 @@
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess(this);
- int res = ActivityManager.getService().bindIsolatedService(
- mMainThread.getApplicationThread(), getActivityToken(), service,
- service.resolveTypeIfNeeded(getContentResolver()),
- sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
+ int res = ActivityManager.getService().bindServiceInstance(
+ mMainThread.getApplicationThread(), getActivityToken(), service,
+ service.resolveTypeIfNeeded(getContentResolver()),
+ sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 82c419a..e4ef12c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -169,7 +169,7 @@
int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String callingPackage, int userId);
- int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
+ int bindServiceInstance(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String instanceName, in String callingPackage, int userId);
void updateServiceGroup(in IServiceConnection connection, int group, int importance);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 1714229..77657d5 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -113,6 +113,7 @@
in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
in String[] args);
+ void dumpResources(in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
void clearDnsCache();
void updateHttpProxy();
void setCoreSettings(in Bundle coreSettings);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9ac4030..a4227a4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -633,7 +633,7 @@
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update
* to the role holder is required.
*
- * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
+ * <p>This result code can be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
*
* @hide
*/
@@ -641,15 +641,19 @@
public static final int RESULT_UPDATE_ROLE_HOLDER = 2;
/**
- * A {@link Bundle} extra which describes the state of the role holder at the time when it
- * returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
+ * A {@link PersistableBundle} extra which the role holder can use to describe its own state
+ * when it returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
*
- * <p>After the update completes, the role holder's {@link
- * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
+ * <p>If {@link #RESULT_UPDATE_ROLE_HOLDER} was accompanied by this extra, after the update
+ * completes, the role holder's {@link #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched,
* which will contain this extra. It is the role holder's responsibility to restore its
* state from this extra.
*
+ * <p>The content of this {@link PersistableBundle} is entirely up to the role holder. It
+ * should contain anything the role holder needs to restore its original state when it gets
+ * restarted.
+ *
* @hide
*/
@SystemApi
@@ -14815,6 +14819,11 @@
* The device may not connect to networks that do not meet the minimum security level.
* If the current network does not meet the minimum security level set, it will be disconnected.
*
+ * The following shows the Wi-Fi security levels from the lowest to the highest security level:
+ * {@link #WIFI_SECURITY_OPEN}
+ * {@link #WIFI_SECURITY_PERSONAL}
+ * {@link #WIFI_SECURITY_ENTERPRISE_EAP}
+ * {@link #WIFI_SECURITY_ENTERPRISE_192}
*
* @param level minimum security level
* @throws SecurityException if the caller is not a device owner or a profile owner on
@@ -14991,8 +15000,8 @@
* <p>Also returns the drawable from {@code defaultDrawableLoader} if
* {@link DevicePolicyResources.Drawables#UNDEFINED} was passed.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
* set a different value use
@@ -15009,7 +15018,7 @@
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15025,8 +15034,8 @@
* {@link #getDrawable(String, String, Callable)}
* if an override was set for that specific source.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15037,7 +15046,7 @@
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15080,8 +15089,8 @@
* Similar to {@link #getDrawable(String, String, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15094,7 +15103,7 @@
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawableForDensity(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15112,8 +15121,8 @@
* Similar to {@link #getDrawable(String, String, String, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
- * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultDrawableLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15127,7 +15136,7 @@
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @NonNull
+ @Nullable
public Drawable getDrawableForDensity(
@NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
@NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
@@ -15166,7 +15175,7 @@
/**
* For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string
* resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID
- * {@code callingPackageResourceId} (see {@link DevicePolicyResources.String}), meaning any
+ * {@code callingPackageResourceId} (see {@link DevicePolicyResources.Strings}), meaning any
* system UI surface calling {@link #getString} with {@code stringId} will get
* the new resource after this API is called.
*
@@ -15202,7 +15211,7 @@
/**
* Removes the updated strings for the list of {@code stringIds} (see
- * {@link DevicePolicyResources.String}) that was previously set by calling {@link #setStrings},
+ * {@link DevicePolicyResources.Strings}) that was previously set by calling {@link #setStrings},
* meaning any subsequent calls to {@link #getString} for the provided IDs will
* return the default string from {@code defaultStringLoader}.
*
@@ -15227,14 +15236,14 @@
/**
* Returns the appropriate updated string for the {@code stringId} (see
- * {@link DevicePolicyResources.String}) if one was set using
+ * {@link DevicePolicyResources.Strings}) if one was set using
* {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}.
*
* <p>Also returns the string from {@code defaultStringLoader} if
- * {@link DevicePolicyResources.String#INVALID_ID} was passed.
+ * {@link DevicePolicyResources.Strings#UNDEFINED} was passed.
*
- * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultStringLoader} returned {@code null}.
*
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
@@ -15249,7 +15258,7 @@
* @hide
*/
@SystemApi
- @NonNull
+ @Nullable
public String getString(
@NonNull @DevicePolicyResources.UpdatableStringId String stringId,
@NonNull Callable<String> defaultStringLoader) {
@@ -15284,8 +15293,8 @@
* {@link java.util.Formatter} and {@link java.lang.String#format}, (see
* {@link Resources#getString(int, Object...)}).
*
- * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
- * {@link NullPointerException} is thrown.
+ * <p>Calls to this API will not return {@code null} unless no updated drawable was found
+ * and the call to {@code defaultStringLoader} returned {@code null}.
*
* @param stringId The IDs to get the updated resource for.
* @param defaultStringLoader To get the default string if no updated string was set for
@@ -15295,7 +15304,7 @@
* @hide
*/
@SystemApi
- @NonNull
+ @Nullable
@SuppressLint("SamShouldBeLast")
public String getString(
@NonNull @DevicePolicyResources.UpdatableStringId String stringId,
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 2ad2010..cf349b0 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -93,6 +93,167 @@
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_WORK_ACCOUNT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCOUNTS_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_THIS_DEVICE_ADMIN_APP;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVE_DEVICE_ADMIN_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT_MINIMUM;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_CAMERA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_LOCATION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_MICROPHONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_COUNT_ESTIMATED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_INSTALLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_NONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_CURRENT_INPUT_METHOD;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_DEFAULT_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_HTTP_PROXY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_INPUT_METHOD_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_LOCK_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_APPS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_BUG_REPORT_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_NETWORK_LOGS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_USAGE_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_WORK_DATA_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_WIPE_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_PERSONAL_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_DATA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_PERMISSIONS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_DEVICE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_PERSONAL_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTACT_YOUR_IT_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_SETTINGS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITHOUT_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITH_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.DISABLED_BY_IT_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ENTERPRISE_PRIVACY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ERROR_MOVE_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_SETTINGS_FOR_WORK_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_UNLOCK_DISABLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_FOR_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.HOW_TO_DISCONNECT_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.IT_ADMIN_POLICY_DISABLING_INFO_URL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_BY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_PROFILE_SETTINGS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGE_DEVICE_ADMIN_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NO_DEVICE_ADMINS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS_NONE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ONLY_CONNECT_TRUSTED_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.OTHER_OPTIONS_DISABLED_BY_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PASSWORD_RECENTLY_USED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_PROFILE_APP_SUBTEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PIN_RECENTLY_USED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_AND_UNINSTALL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SELECT_DEVICE_ADMIN_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_POSTSETUP_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PASSWORD_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PATTERN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PIN_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_DIALOG_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARING_REMOTE_BUGREPORT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.UNINSTALL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.USER_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ADMIN_POLICIES_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ALARM_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_APP_SUBTEXT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_KEYBOARDS_AND_TOOLS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCATION_SWITCH_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCKED_NOTIFICATION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_ATTEMPTS_FAILED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_MANAGED_BY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOT_AVAILABLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_OFF_CONDITION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PASSWORD_REQUIRED;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_RINGTONE_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SECURITY_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_OFF_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_ON_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.YOUR_ACCESS_TO_THIS_DEVICE_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
@@ -159,7 +320,8 @@
Drawables.WORK_PROFILE_OFF_ICON,
Drawables.WORK_PROFILE_USER_ICON
})
- public @interface UpdatableDrawableId {}
+ public @interface UpdatableDrawableId {
+ }
/**
* Identifiers to specify the desired style for the updatable device management system
@@ -174,7 +336,8 @@
Drawables.Style.OUTLINE,
Drawables.Style.DEFAULT
})
- public @interface UpdatableDrawableStyle {}
+ public @interface UpdatableDrawableStyle {
+ }
/**
* Identifiers to specify the location if the updatable device management system resource.
@@ -191,7 +354,8 @@
Drawables.Source.QUICK_SETTINGS,
Drawables.Source.STATUS_BAR
})
- public @interface UpdatableDrawableSource {}
+ public @interface UpdatableDrawableSource {
+ }
/**
* Resource identifiers used to update device management-related string resources.
@@ -231,7 +395,7 @@
PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE,
PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE,
- NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
+ NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN,
SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK,
FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB,
@@ -257,7 +421,86 @@
SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE,
BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE,
BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE,
- WORK_PROFILE_PAUSED_MESSAGE
+ WORK_PROFILE_PAUSED_MESSAGE,
+
+ // Settings Strings
+ FACE_SETTINGS_FOR_WORK_TITLE, WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE,
+ WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK, WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE,
+ WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE,
+ WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE,
+ WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE,
+ WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE, WORK_PROFILE_LOCK_ATTEMPTS_FAILED,
+ ACCESSIBILITY_CATEGORY_WORK, ACCESSIBILITY_CATEGORY_PERSONAL,
+ ACCESSIBILITY_WORK_ACCOUNT_TITLE, ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE,
+ WORK_PROFILE_LOCATION_SWITCH_TITLE, SET_WORK_PROFILE_PASSWORD_HEADER,
+ SET_WORK_PROFILE_PIN_HEADER, SET_WORK_PROFILE_PATTERN_HEADER,
+ CONFIRM_WORK_PROFILE_PASSWORD_HEADER, CONFIRM_WORK_PROFILE_PIN_HEADER,
+ CONFIRM_WORK_PROFILE_PATTERN_HEADER, REENTER_WORK_PROFILE_PASSWORD_HEADER,
+ REENTER_WORK_PROFILE_PIN_HEADER, WORK_PROFILE_CONFIRM_PATTERN, WORK_PROFILE_CONFIRM_PIN,
+ WORK_PROFILE_PASSWORD_REQUIRED, WORK_PROFILE_SECURITY_TITLE,
+ WORK_PROFILE_UNIFY_LOCKS_TITLE, WORK_PROFILE_UNIFY_LOCKS_SUMMARY,
+ WORK_PROFILE_UNIFY_LOCKS_DETAIL, WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT,
+ WORK_PROFILE_KEYBOARDS_AND_TOOLS, WORK_PROFILE_NOT_AVAILABLE, WORK_PROFILE_SETTING,
+ WORK_PROFILE_SETTING_ON_SUMMARY, WORK_PROFILE_SETTING_OFF_SUMMARY, REMOVE_WORK_PROFILE,
+ DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING,
+ WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING, WORK_PROFILE_CONFIRM_REMOVE_TITLE,
+ WORK_PROFILE_CONFIRM_REMOVE_MESSAGE, WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS,
+ WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER, WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE,
+ WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY, WORK_PROFILE_RINGTONE_TITLE,
+ WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE, WORK_PROFILE_ALARM_RINGTONE_TITLE,
+ WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY,
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE,
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE,
+ WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER, WORK_PROFILE_LOCKED_NOTIFICATION_TITLE,
+ WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE,
+ WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY,
+ WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED, CONNECTED_WORK_AND_PERSONAL_APPS_TITLE,
+ CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA, ONLY_CONNECT_TRUSTED_APPS,
+ HOW_TO_DISCONNECT_APPS, CONNECT_APPS_DIALOG_TITLE, CONNECT_APPS_DIALOG_SUMMARY,
+ APP_CAN_ACCESS_PERSONAL_DATA, APP_CAN_ACCESS_PERSONAL_PERMISSIONS,
+ INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT,
+ INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT, WORK_PROFILE_MANAGED_BY, MANAGED_BY,
+ WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING, DISABLED_BY_IT_ADMIN_TITLE,
+ CONTACT_YOUR_IT_ADMIN, WORK_PROFILE_ADMIN_POLICIES_WARNING, USER_ADMIN_POLICIES_WARNING,
+ DEVICE_ADMIN_POLICIES_WARNING, WORK_PROFILE_OFF_CONDITION_TITLE,
+ MANAGED_PROFILE_SETTINGS_TITLE, WORK_PROFILE_CONTACT_SEARCH_TITLE,
+ WORK_PROFILE_CONTACT_SEARCH_SUMMARY, CROSS_PROFILE_CALENDAR_TITLE,
+ CROSS_PROFILE_CALENDAR_SUMMARY, ALWAYS_ON_VPN_PERSONAL_PROFILE, ALWAYS_ON_VPN_DEVICE,
+ ALWAYS_ON_VPN_WORK_PROFILE, CA_CERTS_PERSONAL_PROFILE, CA_CERTS_WORK_PROFILE,
+ CA_CERTS_DEVICE, ADMIN_CAN_LOCK_DEVICE, ADMIN_CAN_WIPE_DEVICE,
+ ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE,
+ ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE, DEVICE_MANAGED_WITHOUT_NAME,
+ DEVICE_MANAGED_WITH_NAME, WORK_PROFILE_APP_SUBTEXT, PERSONAL_PROFILE_APP_SUBTEXT,
+ FINGERPRINT_FOR_WORK, FACE_UNLOCK_DISABLED, FINGERPRINT_UNLOCK_DISABLED,
+ FINGERPRINT_UNLOCK_DISABLED_EXPLANATION, PIN_RECENTLY_USED, PASSWORD_RECENTLY_USED,
+ MANAGE_DEVICE_ADMIN_APPS, NUMBER_OF_DEVICE_ADMINS_NONE, NUMBER_OF_DEVICE_ADMINS,
+ FORGOT_PASSWORD_TITLE, FORGOT_PASSWORD_TEXT, ERROR_MOVE_DEVICE_ADMIN,
+ DEVICE_ADMIN_SETTINGS_TITLE, REMOVE_DEVICE_ADMIN, UNINSTALL_DEVICE_ADMIN,
+ REMOVE_AND_UNINSTALL_DEVICE_ADMIN, SELECT_DEVICE_ADMIN_APPS, NO_DEVICE_ADMINS,
+ ACTIVATE_DEVICE_ADMIN_APP, ACTIVATE_THIS_DEVICE_ADMIN_APP,
+ ACTIVATE_DEVICE_ADMIN_APP_TITLE, NEW_DEVICE_ADMIN_WARNING,
+ NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED, ACTIVE_DEVICE_ADMIN_WARNING,
+ SET_PROFILE_OWNER_TITLE, SET_PROFILE_OWNER_DIALOG_TITLE,
+ SET_PROFILE_OWNER_POSTSETUP_WARNING, OTHER_OPTIONS_DISABLED_BY_ADMIN,
+ REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION, IT_ADMIN_POLICY_DISABLING_INFO_URL,
+ SHARE_REMOTE_BUGREPORT_DIALOG_TITLE, SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT,
+ SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT, SHARING_REMOTE_BUGREPORT_MESSAGE,
+ MANAGED_DEVICE_INFO, MANAGED_DEVICE_INFO_SUMMARY, MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME,
+ ENTERPRISE_PRIVACY_HEADER, INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE,
+ CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE, YOUR_ACCESS_TO_THIS_DEVICE_TITLE,
+ ADMIN_CAN_SEE_WORK_DATA_WARNING, ADMIN_CAN_SEE_APPS_WARNING,
+ ADMIN_CAN_SEE_USAGE_WARNING, ADMIN_CAN_SEE_NETWORK_LOGS_WARNING,
+ ADMIN_CAN_SEE_BUG_REPORT_WARNING, ADMIN_CAN_SEE_SECURITY_LOGS_WARNING,
+ ADMIN_ACTION_NONE, ADMIN_ACTION_APPS_INSTALLED, ADMIN_ACTION_APPS_COUNT_ESTIMATED,
+ ADMIN_ACTIONS_APPS_COUNT_MINIMUM, ADMIN_ACTION_ACCESS_LOCATION,
+ ADMIN_ACTION_ACCESS_MICROPHONE, ADMIN_ACTION_ACCESS_CAMERA,
+ ADMIN_ACTION_SET_DEFAULT_APPS, ADMIN_ACTIONS_APPS_COUNT,
+ ADMIN_ACTION_SET_CURRENT_INPUT_METHOD, ADMIN_ACTION_SET_INPUT_METHOD_NAME,
+ ADMIN_ACTION_SET_HTTP_PROXY, WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY,
+ WORK_PROFILE_PRIVACY_POLICY_INFO, CONNECTED_APPS_SEARCH_KEYWORDS,
+ WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS, ACCOUNTS_SEARCH_KEYWORDS,
+ CONTROLLED_BY_ADMIN_SUMMARY, WORK_PROFILE_USER_LABEL, WORK_CATEGORY_HEADER,
+ PERSONAL_CATEGORY_HEADER
})
public @interface UpdatableStringId {
}
@@ -432,7 +675,8 @@
@SystemApi
public static final class Strings {
- private Strings() {}
+ private Strings() {
+ }
/**
* An ID for any string that can't be updated.
@@ -456,13 +700,1209 @@
/**
* Class containing the identifiers used to update device management-related system strings
+ * in the Settings package
+ *
+ * @hide
+ */
+ public static final class Settings {
+
+ private Settings() {
+ }
+
+ private static final String PREFIX = "Settings.";
+
+ /**
+ * Title shown for menu item that launches face settings or enrollment, for work profile
+ */
+ public static final String FACE_SETTINGS_FOR_WORK_TITLE =
+ PREFIX + "FACE_SETTINGS_FOR_WORK_TITLE";
+
+ /**
+ * Warning when removing the last fingerprint on a work profile
+ */
+ public static final String WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE =
+ PREFIX + "WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE";
+
+ /**
+ * Text letting the user know that their IT admin can't reset their screen lock if they
+ * forget it, and they can choose to set another lock that would be specifically for
+ * their work apps
+ */
+ public static final String WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK =
+ PREFIX + "WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK";
+
+ /**
+ * Message shown in screen lock picker for setting up a work profile screen lock
+ */
+ public static final String WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE =
+ PREFIX + "WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE";
+
+ /**
+ * Title for PreferenceScreen to launch picker for security method for the managed
+ * profile when there is none
+ */
+ public static final String WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE =
+ PREFIX + "WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user only has one attempt left to provide the
+ * work lock pattern before the work profile is removed
+ */
+ public static final String WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE =
+ PREFIX + "WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE";
+
+ /**
+ * Content of the dialog shown when the user has failed to provide the device lock too
+ * many times and the device is wiped
+ */
+ public static final String WORK_PROFILE_LOCK_ATTEMPTS_FAILED =
+ PREFIX + "WORK_PROFILE_LOCK_ATTEMPTS_FAILED";
+
+ /**
+ * Content description for work profile accounts group
+ */
+ public static final String ACCESSIBILITY_CATEGORY_WORK =
+ PREFIX + "ACCESSIBILITY_CATEGORY_WORK";
+
+ /**
+ * Content description for personal profile accounts group
+ */
+ public static final String ACCESSIBILITY_CATEGORY_PERSONAL =
+ PREFIX + "ACCESSIBILITY_CATEGORY_PERSONAL";
+
+ /**
+ * Content description for work profile details page title
+ */
+ public static final String ACCESSIBILITY_WORK_ACCOUNT_TITLE =
+ PREFIX + "ACCESSIBILITY_WORK_ACCOUNT_TITLE";
+
+ /**
+ * Content description for personal profile details page title
+ */
+ public static final String ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE =
+ PREFIX + "ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE";
+
+ /**
+ * Title for work profile location switch
+ */
+ public static final String WORK_PROFILE_LOCATION_SWITCH_TITLE =
+ PREFIX + "WORK_PROFILE_LOCATION_SWITCH_TITLE";
+
+ /**
+ * Header when setting work profile password
+ */
+ public static final String SET_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when setting work profile PIN
+ */
+ public static final String SET_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Header when setting work profile pattern
+ */
+ public static final String SET_WORK_PROFILE_PATTERN_HEADER =
+ PREFIX + "SET_WORK_PROFILE_PATTERN_HEADER";
+
+ /**
+ * Header when confirming work profile password
+ */
+ public static final String CONFIRM_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when confirming work profile pin
+ */
+ public static final String CONFIRM_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Header when confirming work profile pattern
+ */
+ public static final String CONFIRM_WORK_PROFILE_PATTERN_HEADER =
+ PREFIX + "CONFIRM_WORK_PROFILE_PATTERN_HEADER";
+
+ /**
+ * Header when re-entering work profile password
+ */
+ public static final String REENTER_WORK_PROFILE_PASSWORD_HEADER =
+ PREFIX + "REENTER_WORK_PROFILE_PASSWORD_HEADER";
+
+ /**
+ * Header when re-entering work profile pin
+ */
+ public static final String REENTER_WORK_PROFILE_PIN_HEADER =
+ PREFIX + "REENTER_WORK_PROFILE_PIN_HEADER";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work pattern to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PATTERN =
+ PREFIX + "WORK_PROFILE_CONFIRM_PATTERN";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work pin to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PIN =
+ PREFIX + "WORK_PROFILE_CONFIRM_PIN";
+
+ /**
+ * Message to be used to explain the users that they need to enter their work password
+ * to
+ * continue a particular operation
+ */
+ public static final String WORK_PROFILE_CONFIRM_PASSWORD =
+ PREFIX + "WORK_PROFILE_CONFIRM_PASSWORD";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pattern
+ * that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PATTERN_REQUIRED =
+ PREFIX + "WORK_PROFILE_PATTERN_REQUIRED";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pin
+ * that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PIN_REQUIRED =
+ PREFIX + "WORK_PROFILE_PIN_REQUIRED";
+
+ /**
+ * This string shows = PREFIX + "shows"; up on a screen where a user can enter a
+ * password that lets them access
+ * their work profile. This is an extra security measure that's required for them to
+ * continue
+ */
+ public static final String WORK_PROFILE_PASSWORD_REQUIRED =
+ PREFIX + "WORK_PROFILE_PASSWORD_REQUIRED";
+
+ /**
+ * Header for Work Profile security settings
+ */
+ public static final String WORK_PROFILE_SECURITY_TITLE =
+ PREFIX + "WORK_PROFILE_SECURITY_TITLE";
+
+ /**
+ * Header for Work Profile unify locks settings
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_TITLE =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_TITLE";
+
+ /**
+ * Setting option explanation to unify work and personal locks
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_SUMMARY =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_SUMMARY";
+
+ /**
+ * Further explanation when the user wants to unify work and personal locks
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_DETAIL =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_DETAIL";
+
+ /**
+ * Ask if the user wants to create a new lock for personal and work as the current work
+ * lock is not enough for the device
+ */
+ public static final String WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT =
+ PREFIX + "WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT";
+
+ /**
+ * Title of 'Work profile keyboards & tools' preference category
+ */
+ public static final String WORK_PROFILE_KEYBOARDS_AND_TOOLS =
+ PREFIX + "WORK_PROFILE_KEYBOARDS_AND_TOOLS";
+
+ /**
+ * Label for state when work profile is not available
+ */
+ public static final String WORK_PROFILE_NOT_AVAILABLE =
+ PREFIX + "WORK_PROFILE_NOT_AVAILABLE";
+
+ /**
+ * Label for work profile setting (to allow turning work profile on and off)
+ */
+ public static final String WORK_PROFILE_SETTING = PREFIX + "WORK_PROFILE_SETTING";
+
+ /**
+ * Description of the work profile setting when the work profile is on
+ */
+ public static final String WORK_PROFILE_SETTING_ON_SUMMARY =
+ PREFIX + "WORK_PROFILE_SETTING_ON_SUMMARY";
+
+ /**
+ * Description of the work profile setting when the work profile is off
+ */
+ public static final String WORK_PROFILE_SETTING_OFF_SUMMARY =
+ PREFIX + "WORK_PROFILE_SETTING_OFF_SUMMARY";
+
+ /**
+ * Button text to remove work profile
+ */
+ public static final String REMOVE_WORK_PROFILE = PREFIX + "REMOVE_WORK_PROFILE";
+
+ /**
+ * Text of message to show to device owner user whose administrator has installed a SSL
+ * CA Cert
+ */
+ public static final String DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING =
+ PREFIX + "DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING";
+
+ /**
+ * Text of message to show to work profile users whose administrator has installed a SSL
+ * CA Cert
+ */
+ public static final String WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING =
+ PREFIX + "WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING";
+
+ /**
+ * Work profile removal confirmation title
+ */
+ public static final String WORK_PROFILE_CONFIRM_REMOVE_TITLE =
+ PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_TITLE";
+
+ /**
+ * Work profile removal confirmation message
+ */
+ public static final String WORK_PROFILE_CONFIRM_REMOVE_MESSAGE =
+ PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_MESSAGE";
+
+ /**
+ * Toast shown when an app in the work profile attempts to open notification settings
+ * and apps in the work profile cannot access notification settings
+ */
+ public static final String WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS =
+ PREFIX + "WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS";
+
+ /**
+ * Work sound settings section header
+ */
+ public static final String WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER =
+ PREFIX + "WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER";
+
+ /**
+ * Title for the switch that enables syncing of personal ringtones to work profile
+ */
+ public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE =
+ PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE";
+
+ /**
+ * Summary for the switch that enables syncing of personal ringtones to work profile
+ */
+ public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY =
+ PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY";
+
+ /**
+ * Title for the option defining the work profile phone ringtone
+ */
+ public static final String WORK_PROFILE_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_RINGTONE_TITLE";
+
+ /**
+ * Title for the option defining the default work profile notification ringtone
+ */
+ public static final String WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE";
+
+ /**
+ * Title for the option defining the default work alarm ringtone
+ */
+ public static final String WORK_PROFILE_ALARM_RINGTONE_TITLE =
+ PREFIX + "WORK_PROFILE_ALARM_RINGTONE_TITLE";
+
+ /**
+ * Summary for sounds when sync with personal sounds is active
+ */
+ public static final String WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY =
+ PREFIX + "WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY";
+
+ /**
+ * Title for dialog shown when enabling sync with personal sounds
+ */
+ public static final String
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE =
+ PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE";
+
+ /**
+ * Message for dialog shown when using the same sounds for work events as for personal
+ * events
+ */
+ public static final String
+ ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE =
+ PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE";
+
+ /**
+ * Work profile notifications section header
+ */
+ public static final String WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER =
+ PREFIX + "WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER";
+
+ /**
+ * Title for the option controlling notifications for work profile
+ */
+ public static final String WORK_PROFILE_LOCKED_NOTIFICATION_TITLE =
+ PREFIX + "WORK_PROFILE_LOCKED_NOTIFICATION_TITLE";
+
+ /**
+ * Title for redacting sensitive content on lockscreen for work profiles
+ */
+ public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE =
+ PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE";
+
+ /**
+ * Summary for redacting sensitive content on lockscreen for work profiles
+ */
+ public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY =
+ PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY";
+
+ /**
+ * Indicates that the work profile admin doesn't allow this notification listener to
+ * access
+ * work profile notifications
+ */
+ public static final String WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED =
+ PREFIX + "WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED";
+
+ /**
+ * This setting shows a user's connected work and personal apps.
+ */
+ public static final String CONNECTED_WORK_AND_PERSONAL_APPS_TITLE =
+ PREFIX + "CONNECTED_WORK_AND_PERSONAL_APPS_TITLE";
+
+ /**
+ * This text lets a user know that if they connect work and personal apps,
+ * they will share permissions and can access each other's data
+ */
+ public static final String CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA =
+ PREFIX + "CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA";
+
+ /**
+ * This text lets a user know that they should only connect work and personal apps if
+ * they
+ * trust the work app with their personal data
+ */
+ public static final String ONLY_CONNECT_TRUSTED_APPS =
+ PREFIX + "ONLY_CONNECT_TRUSTED_APPS";
+
+ /**
+ * This text lets a user know how to disconnect work and personal apps
+ */
+ public static final String HOW_TO_DISCONNECT_APPS = PREFIX + "HOW_TO_DISCONNECT_APPS";
+
+ /**
+ * Title of confirmation dialog when connecting work and personal apps
+ */
+ public static final String CONNECT_APPS_DIALOG_TITLE =
+ PREFIX + "CONNECT_APPS_DIALOG_TITLE";
+
+ /**
+ * This dialog is shown when a user tries to connect a work app to a personal
+ * app
+ */
+ public static final String CONNECT_APPS_DIALOG_SUMMARY =
+ PREFIX + "CONNECT_APPS_DIALOG_SUMMARY";
+
+ /**
+ * This text lets the user know that their work app will be able to access data in their
+ * personal app
+ */
+ public static final String APP_CAN_ACCESS_PERSONAL_DATA =
+ PREFIX + "APP_CAN_ACCESS_PERSONAL_DATA";
+
+ /**
+ * This text lets the user know that their work app will be able to use permissions in
+ * their personal app
+ */
+ public static final String APP_CAN_ACCESS_PERSONAL_PERMISSIONS =
+ PREFIX + "APP_CAN_ACCESS_PERSONAL_PERMISSIONS";
+
+ /**
+ * lets a user know that they need to install an app in their work profile in order to
+ * connect it to the corresponding personal app
+ */
+ public static final String INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT =
+ PREFIX + "INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT";
+
+ /**
+ * lets a user know that they need to install an app in their personal profile in order
+ * to
+ * connect it to the corresponding work app
+ */
+ public static final String INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT =
+ PREFIX + "INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT";
+
+ /**
+ * Header for showing the organisation managing the work profile
+ */
+ public static final String WORK_PROFILE_MANAGED_BY = PREFIX + "WORK_PROFILE_MANAGED_BY";
+
+ /**
+ * Summary showing the enterprise who manages the device or profile.
+ */
+ public static final String MANAGED_BY = PREFIX + "MANAGED_BY";
+
+ /**
+ * Warning message about disabling usage access on profile owner
+ */
+ public static final String WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING =
+ PREFIX + "WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING";
+
+ /**
+ * Title for dialog displayed when user taps a setting on their phone that's blocked by
+ * their IT admin
+ */
+ public static final String DISABLED_BY_IT_ADMIN_TITLE =
+ PREFIX + "DISABLED_BY_IT_ADMIN_TITLE";
+
+ /**
+ * Shown when the user tries to change phone settings that are blocked by their IT admin
+ */
+ public static final String CONTACT_YOUR_IT_ADMIN = PREFIX + "CONTACT_YOUR_IT_ADMIN";
+
+ /**
+ * warn user about policies the admin can set in a work profile
+ */
+ public static final String WORK_PROFILE_ADMIN_POLICIES_WARNING =
+ PREFIX + "WORK_PROFILE_ADMIN_POLICIES_WARNING";
+
+ /**
+ * warn user about policies the admin can set on a user
+ */
+ public static final String USER_ADMIN_POLICIES_WARNING =
+ PREFIX + "USER_ADMIN_POLICIES_WARNING";
+
+ /**
+ * warn user about policies the admin can set on a device
+ */
+ public static final String DEVICE_ADMIN_POLICIES_WARNING =
+ PREFIX + "DEVICE_ADMIN_POLICIES_WARNING";
+
+ /**
+ * Condition that work profile is off
+ */
+ public static final String WORK_PROFILE_OFF_CONDITION_TITLE =
+ PREFIX + "WORK_PROFILE_OFF_CONDITION_TITLE";
+
+ /**
+ * Title of work profile setting page
+ */
+ public static final String MANAGED_PROFILE_SETTINGS_TITLE =
+ PREFIX + "MANAGED_PROFILE_SETTINGS_TITLE";
+
+ /**
+ * Setting that lets a user's personal apps identify contacts using the user's work
+ * directory
+ */
+ public static final String WORK_PROFILE_CONTACT_SEARCH_TITLE =
+ PREFIX + "WORK_PROFILE_CONTACT_SEARCH_TITLE";
+
+ /**
+ * This setting lets a user's personal apps identify contacts using the user's work
+ * directory
+ */
+ public static final String WORK_PROFILE_CONTACT_SEARCH_SUMMARY =
+ PREFIX + "WORK_PROFILE_CONTACT_SEARCH_SUMMARY";
+
+ /**
+ * This setting lets the user show their work events on their personal calendar
+ */
+ public static final String CROSS_PROFILE_CALENDAR_TITLE =
+ PREFIX + "CROSS_PROFILE_CALENDAR_TITLE";
+
+ /**
+ * Setting description. If the user turns on this setting, they can see their work
+ * events on their personal calendar
+ */
+ public static final String CROSS_PROFILE_CALENDAR_SUMMARY =
+ PREFIX + "CROSS_PROFILE_CALENDAR_SUMMARY";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin in the personal profile
+ */
+ public static final String ALWAYS_ON_VPN_PERSONAL_PROFILE =
+ PREFIX + "ALWAYS_ON_VPN_PERSONAL_PROFILE";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin for the entire device
+ */
+ public static final String ALWAYS_ON_VPN_DEVICE = PREFIX + "ALWAYS_ON_VPN_DEVICE";
+
+ /**
+ * Label explaining that an always-on VPN was set by the admin in the work profile
+ */
+ public static final String ALWAYS_ON_VPN_WORK_PROFILE =
+ PREFIX + "ALWAYS_ON_VPN_WORK_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates in personal profile
+ */
+ public static final String CA_CERTS_PERSONAL_PROFILE =
+ PREFIX + "CA_CERTS_PERSONAL_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates in work profile
+ */
+ public static final String CA_CERTS_WORK_PROFILE = PREFIX + "CA_CERTS_WORK_PROFILE";
+
+ /**
+ * Label explaining that the admin installed trusted CA certificates for the entire
+ * device
+ */
+ public static final String CA_CERTS_DEVICE = PREFIX + "CA_CERTS_DEVICE";
+
+ /**
+ * Label explaining that the admin can lock the device and change the user's password
+ */
+ public static final String ADMIN_CAN_LOCK_DEVICE = PREFIX + "ADMIN_CAN_LOCK_DEVICE";
+
+ /**
+ * Label explaining that the admin can wipe the device remotely
+ */
+ public static final String ADMIN_CAN_WIPE_DEVICE = PREFIX + "ADMIN_CAN_WIPE_DEVICE";
+
+ /**
+ * Label explaining that the admin configured the device to wipe itself when the
+ * password is mistyped too many times
+ */
+ public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE =
+ PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE";
+
+ /**
+ * Label explaining that the admin configured the work profile to wipe itself when the
+ * password is mistyped too many times
+ */
+ public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE =
+ PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE";
+
+ /**
+ * Message indicating that the device is enterprise-managed by a Device Owner
+ */
+ public static final String DEVICE_MANAGED_WITHOUT_NAME =
+ PREFIX + "DEVICE_MANAGED_WITHOUT_NAME";
+
+ /**
+ * Message indicating that the device is enterprise-managed by a Device Owner
+ */
+ public static final String DEVICE_MANAGED_WITH_NAME =
+ PREFIX + "DEVICE_MANAGED_WITH_NAME";
+
+ /**
+ * Subtext of work profile app for current setting
+ */
+ public static final String WORK_PROFILE_APP_SUBTEXT =
+ PREFIX + "WORK_PROFILE_APP_SUBTEXT";
+
+ /**
+ * Subtext of personal profile app for current setting
+ */
+ public static final String PERSONAL_PROFILE_APP_SUBTEXT =
+ PREFIX + "PERSONAL_PROFILE_APP_SUBTEXT";
+
+ /**
+ * Title shown for work menu item that launches fingerprint settings or enrollment
+ */
+ public static final String FINGERPRINT_FOR_WORK = PREFIX + "FINGERPRINT_FOR_WORK";
+
+ /**
+ * Message shown in face enrollment dialog, when face unlock is disabled by device admin
+ */
+ public static final String FACE_UNLOCK_DISABLED = PREFIX + "FACE_UNLOCK_DISABLED";
+
+ /**
+ * message shown in fingerprint enrollment dialog, when fingerprint unlock is disabled
+ * by device admin
+ */
+ public static final String FINGERPRINT_UNLOCK_DISABLED =
+ PREFIX + "FINGERPRINT_UNLOCK_DISABLED";
+
+ /**
+ * Text shown in fingerprint settings explaining what the fingerprint can be used for in
+ * the case unlocking is disabled
+ */
+ public static final String FINGERPRINT_UNLOCK_DISABLED_EXPLANATION =
+ PREFIX + "FINGERPRINT_UNLOCK_DISABLED_EXPLANATION";
+
+ /**
+ * Error shown when in PIN mode and PIN has been used recently
+ */
+ public static final String PIN_RECENTLY_USED = PREFIX + "PIN_RECENTLY_USED";
+
+ /**
+ * Error shown when in PASSWORD mode and password has been used recently
+ */
+ public static final String PASSWORD_RECENTLY_USED = PREFIX + "PASSWORD_RECENTLY_USED";
+
+ /**
+ * Title of preference to manage device admin apps
+ */
+ public static final String MANAGE_DEVICE_ADMIN_APPS =
+ PREFIX + "MANAGE_DEVICE_ADMIN_APPS";
+
+ /**
+ * Inform the user that currently no device admin apps are installed and active
+ */
+ public static final String NUMBER_OF_DEVICE_ADMINS_NONE =
+ PREFIX + "NUMBER_OF_DEVICE_ADMINS_NONE";
+
+ /**
+ * Inform the user how many device admin apps are installed and active
+ */
+ public static final String NUMBER_OF_DEVICE_ADMINS = PREFIX + "NUMBER_OF_DEVICE_ADMINS";
+
+ /**
+ * Title that asks the user to contact the IT admin to reset password
+ */
+ public static final String FORGOT_PASSWORD_TITLE = PREFIX + "FORGOT_PASSWORD_TITLE";
+
+ /**
+ * Content that asks the user to contact the IT admin to reset password
+ */
+ public static final String FORGOT_PASSWORD_TEXT = PREFIX + "FORGOT_PASSWORD_TEXT";
+
+ /**
+ * Error message shown when trying to move device administrators to external disks, such
+ * as SD card
+ */
+ public static final String ERROR_MOVE_DEVICE_ADMIN = PREFIX + "ERROR_MOVE_DEVICE_ADMIN";
+
+ /**
+ * Device admin app settings title
+ */
+ public static final String DEVICE_ADMIN_SETTINGS_TITLE =
+ PREFIX + "DEVICE_ADMIN_SETTINGS_TITLE";
+
+ /**
+ * Button to remove the active device admin app
+ */
+ public static final String REMOVE_DEVICE_ADMIN = PREFIX + "REMOVE_DEVICE_ADMIN";
+
+ /**
+ * Button to uninstall the device admin app
+ */
+ public static final String UNINSTALL_DEVICE_ADMIN = PREFIX + "UNINSTALL_DEVICE_ADMIN";
+
+ /**
+ * Button to deactivate and uninstall the device admin app
+ */
+ public static final String REMOVE_AND_UNINSTALL_DEVICE_ADMIN =
+ PREFIX + "REMOVE_AND_UNINSTALL_DEVICE_ADMIN";
+
+ /**
+ * Title for selecting device admin apps
+ */
+ public static final String SELECT_DEVICE_ADMIN_APPS =
+ PREFIX + "SELECT_DEVICE_ADMIN_APPS";
+
+ /**
+ * Message when there are no available device admin apps to display
+ */
+ public static final String NO_DEVICE_ADMINS = PREFIX + "NO_DEVICE_ADMINS";
+
+ /**
+ * Title for screen to add a device admin app
+ */
+ public static final String ACTIVATE_DEVICE_ADMIN_APP =
+ PREFIX + "ACTIVATE_DEVICE_ADMIN_APP";
+
+ /**
+ * Label for button to set the active device admin
+ */
+ public static final String ACTIVATE_THIS_DEVICE_ADMIN_APP =
+ PREFIX + "ACTIVATE_THIS_DEVICE_ADMIN_APP";
+
+ /**
+ * Activate a specific device admin app title
+ */
+ public static final String ACTIVATE_DEVICE_ADMIN_APP_TITLE =
+ PREFIX + "ACTIVATE_DEVICE_ADMIN_APP_TITLE";
+
+ /**
+ * Device admin warning message about policies a not active admin can use
+ */
+ public static final String NEW_DEVICE_ADMIN_WARNING =
+ PREFIX + "NEW_DEVICE_ADMIN_WARNING";
+
+ /**
+ * Simplified device admin warning message
+ */
+ public static final String NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED =
+ PREFIX + "NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED";
+
+ /**
+ * Device admin warning message about policies the active admin can use
+ */
+ public static final String ACTIVE_DEVICE_ADMIN_WARNING =
+ PREFIX + "ACTIVE_DEVICE_ADMIN_WARNING";
+
+ /**
+ * Title for screen to set a profile owner
+ */
+ public static final String SET_PROFILE_OWNER_TITLE = PREFIX + "SET_PROFILE_OWNER_TITLE";
+
+ /**
+ * Simplified title for dialog to set a profile owner
+ */
+ public static final String SET_PROFILE_OWNER_DIALOG_TITLE =
+ PREFIX + "SET_PROFILE_OWNER_DIALOG_TITLE";
+
+ /**
+ * Warning when trying to add a profile owner admin after setup has completed
+ */
+ public static final String SET_PROFILE_OWNER_POSTSETUP_WARNING =
+ PREFIX + "SET_PROFILE_OWNER_POSTSETUP_WARNING";
+
+ /**
+ * Message displayed to let the user know that some of the options are disabled by admin
+ */
+ public static final String OTHER_OPTIONS_DISABLED_BY_ADMIN =
+ PREFIX + "OTHER_OPTIONS_DISABLED_BY_ADMIN";
+
+ /**
+ * This is shown if the authenticator for a given account fails to remove it due to
+ * admin restrictions
+ */
+ public static final String REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION =
+ PREFIX + "REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION";
+
+ /**
+ * Url for learning more about IT admin policy disabling
+ */
+ public static final String IT_ADMIN_POLICY_DISABLING_INFO_URL =
+ PREFIX + "IT_ADMIN_POLICY_DISABLING_INFO_URL";
+
+ /**
+ * Title of dialog shown to ask for user consent for sharing a bugreport that was
+ * requested
+ * remotely by the IT administrator
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_DIALOG_TITLE =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_DIALOG_TITLE";
+
+ /**
+ * Message of a dialog shown to ask for user consent for sharing a bugreport that was
+ * requested remotely by the IT administrator
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT";
+
+ /**
+ * Message of a dialog shown to ask for user consent for sharing a bugreport that was
+ * requested remotely by the IT administrator and it's still being taken
+ */
+ public static final String SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT =
+ PREFIX + "SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT";
+
+ /**
+ * Message of a dialog shown to inform that the remote bugreport that was requested
+ * remotely by the IT administrator is still being taken and will be shared when
+ * finished
+ */
+ public static final String SHARING_REMOTE_BUGREPORT_MESSAGE =
+ PREFIX + "SHARING_REMOTE_BUGREPORT_MESSAGE";
+
+ /**
+ * Managed device information screen title
+ */
+ public static final String MANAGED_DEVICE_INFO = PREFIX + "MANAGED_DEVICE_INFO";
+
+ /**
+ * Summary for managed device info section
+ */
+ public static final String MANAGED_DEVICE_INFO_SUMMARY =
+ PREFIX + "MANAGED_DEVICE_INFO_SUMMARY";
+
+ /**
+ * Summary for managed device info section including organization name
+ */
+ public static final String MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME =
+ PREFIX + "MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME";
+
+ /**
+ * Enterprise Privacy settings header, summarizing the powers that the admin has
+ */
+ public static final String ENTERPRISE_PRIVACY_HEADER =
+ PREFIX + "ENTERPRISE_PRIVACY_HEADER";
+
+ /**
+ * Types of information your organization can see section title
+ */
+ public static final String INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE =
+ PREFIX + "INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE";
+
+ /**
+ * Changes made by your organization's admin section title
+ */
+ public static final String CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE =
+ PREFIX + "CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE";
+
+ /**
+ * Your access to this device section title
+ */
+ public static final String YOUR_ACCESS_TO_THIS_DEVICE_TITLE =
+ PREFIX + "YOUR_ACCESS_TO_THIS_DEVICE_TITLE";
+
+ /**
+ * Things the admin can see: data associated with the work account
+ */
+ public static final String ADMIN_CAN_SEE_WORK_DATA_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_WORK_DATA_WARNING";
+
+ /**
+ * Things the admin can see: Apps installed on the device
+ */
+ public static final String ADMIN_CAN_SEE_APPS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_APPS_WARNING";
+
+ /**
+ * Things the admin can see: Amount of time and data spent in each app
+ */
+ public static final String ADMIN_CAN_SEE_USAGE_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_USAGE_WARNING";
+
+ /**
+ * Things the admin can see: Most recent network traffic log
+ */
+ public static final String ADMIN_CAN_SEE_NETWORK_LOGS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_NETWORK_LOGS_WARNING";
+ /**
+ * Things the admin can see: Most recent bug report
+ */
+ public static final String ADMIN_CAN_SEE_BUG_REPORT_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_BUG_REPORT_WARNING";
+
+ /**
+ * Things the admin can see: Security logs
+ */
+ public static final String ADMIN_CAN_SEE_SECURITY_LOGS_WARNING =
+ PREFIX + "ADMIN_CAN_SEE_SECURITY_LOGS_WARNING";
+
+ /**
+ * Indicate that the admin never took a given action so far (e.g. did not retrieve
+ * security logs or request bug reports).
+ */
+ public static final String ADMIN_ACTION_NONE = PREFIX + "ADMIN_ACTION_NONE";
+
+ /**
+ * Indicate that the admin installed one or more apps on the device
+ */
+ public static final String ADMIN_ACTION_APPS_INSTALLED =
+ PREFIX + "ADMIN_ACTION_APPS_INSTALLED";
+
+ /**
+ * Explaining that the number of apps is an estimation
+ */
+ public static final String ADMIN_ACTION_APPS_COUNT_ESTIMATED =
+ PREFIX + "ADMIN_ACTION_APPS_COUNT_ESTIMATED";
+
+ /**
+ * Indicating the minimum number of apps that a label refers to
+ */
+ public static final String ADMIN_ACTIONS_APPS_COUNT_MINIMUM =
+ PREFIX + "ADMIN_ACTIONS_APPS_COUNT_MINIMUM";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the device's location
+ */
+ public static final String ADMIN_ACTION_ACCESS_LOCATION =
+ PREFIX + "ADMIN_ACTION_ACCESS_LOCATION";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the microphone
+ */
+ public static final String ADMIN_ACTION_ACCESS_MICROPHONE =
+ PREFIX + "ADMIN_ACTION_ACCESS_MICROPHONE";
+
+ /**
+ * Indicate that the admin granted one or more apps access to the camera
+ */
+ public static final String ADMIN_ACTION_ACCESS_CAMERA =
+ PREFIX + "ADMIN_ACTION_ACCESS_CAMERA";
+
+ /**
+ * Indicate that the admin set one or more apps as defaults for common actions
+ */
+ public static final String ADMIN_ACTION_SET_DEFAULT_APPS =
+ PREFIX + "ADMIN_ACTION_SET_DEFAULT_APPS";
+
+ /**
+ * Indicate the number of apps that a label refers to
+ */
+ public static final String ADMIN_ACTIONS_APPS_COUNT =
+ PREFIX + "ADMIN_ACTIONS_APPS_COUNT";
+
+ /**
+ * Indicate that the current input method was set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_CURRENT_INPUT_METHOD =
+ PREFIX + "ADMIN_ACTION_SET_CURRENT_INPUT_METHOD";
+
+ /**
+ * The input method set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_INPUT_METHOD_NAME =
+ PREFIX + "ADMIN_ACTION_SET_INPUT_METHOD_NAME";
+
+ /**
+ * Indicate that a global HTTP proxy was set by the admin
+ */
+ public static final String ADMIN_ACTION_SET_HTTP_PROXY =
+ PREFIX + "ADMIN_ACTION_SET_HTTP_PROXY";
+
+ /**
+ * Summary for Enterprise Privacy settings, explaining what the user can expect to find
+ * under it
+ */
+ public static final String WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY =
+ PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY";
+
+ /**
+ * Setting on privacy settings screen that will show work policy info
+ */
+ public static final String WORK_PROFILE_PRIVACY_POLICY_INFO =
+ PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO";
+
+ /**
+ * Search keywords for connected work and personal apps
+ */
+ public static final String CONNECTED_APPS_SEARCH_KEYWORDS =
+ PREFIX + "CONNECTED_APPS_SEARCH_KEYWORDS";
+
+ /**
+ * Work profile unification keywords
+ */
+ public static final String WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS =
+ PREFIX + "WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS";
+
+ /**
+ * Accounts keywords
+ */
+ public static final String ACCOUNTS_SEARCH_KEYWORDS =
+ PREFIX + "ACCOUNTS_SEARCH_KEYWORDS";
+
+ /**
+ * Summary for settings preference disabled by administrator
+ */
+ public static final String CONTROLLED_BY_ADMIN_SUMMARY =
+ PREFIX + "CONTROLLED_BY_ADMIN_SUMMARY";
+
+ /**
+ * User label for a work profile
+ */
+ public static final String WORK_PROFILE_USER_LABEL = PREFIX + "WORK_PROFILE_USER_LABEL";
+
+ /**
+ * Header for items under the work user
+ */
+ public static final String WORK_CATEGORY_HEADER = PREFIX + "WORK_CATEGORY_HEADER";
+
+ /**
+ * Header for items under the personal user
+ */
+ public static final String PERSONAL_CATEGORY_HEADER = PREFIX + "category_personal";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(FACE_SETTINGS_FOR_WORK_TITLE);
+ strings.add(WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE);
+ strings.add(WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK);
+ strings.add(WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE);
+ strings.add(WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE);
+ strings.add(WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE);
+ strings.add(WORK_PROFILE_LOCK_ATTEMPTS_FAILED);
+ strings.add(ACCESSIBILITY_CATEGORY_WORK);
+ strings.add(ACCESSIBILITY_CATEGORY_PERSONAL);
+ strings.add(ACCESSIBILITY_WORK_ACCOUNT_TITLE);
+ strings.add(ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE);
+ strings.add(WORK_PROFILE_LOCATION_SWITCH_TITLE);
+ strings.add(SET_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(SET_WORK_PROFILE_PIN_HEADER);
+ strings.add(SET_WORK_PROFILE_PATTERN_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PIN_HEADER);
+ strings.add(CONFIRM_WORK_PROFILE_PATTERN_HEADER);
+ strings.add(REENTER_WORK_PROFILE_PASSWORD_HEADER);
+ strings.add(REENTER_WORK_PROFILE_PIN_HEADER);
+ strings.add(WORK_PROFILE_CONFIRM_PATTERN);
+ strings.add(WORK_PROFILE_CONFIRM_PIN);
+ strings.add(WORK_PROFILE_PASSWORD_REQUIRED);
+ strings.add(WORK_PROFILE_SECURITY_TITLE);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_TITLE);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_SUMMARY);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_DETAIL);
+ strings.add(WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT);
+ strings.add(WORK_PROFILE_KEYBOARDS_AND_TOOLS);
+ strings.add(WORK_PROFILE_NOT_AVAILABLE);
+ strings.add(WORK_PROFILE_SETTING);
+ strings.add(WORK_PROFILE_SETTING_ON_SUMMARY);
+ strings.add(WORK_PROFILE_SETTING_OFF_SUMMARY);
+ strings.add(REMOVE_WORK_PROFILE);
+ strings.add(DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING);
+ strings.add(WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING);
+ strings.add(WORK_PROFILE_CONFIRM_REMOVE_TITLE);
+ strings.add(WORK_PROFILE_CONFIRM_REMOVE_MESSAGE);
+ strings.add(WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS);
+ strings.add(WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER);
+ strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE);
+ strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY);
+ strings.add(WORK_PROFILE_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_ALARM_RINGTONE_TITLE);
+ strings.add(WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY);
+ strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE);
+ strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE);
+ strings.add(WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER);
+ strings.add(WORK_PROFILE_LOCKED_NOTIFICATION_TITLE);
+ strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE);
+ strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY);
+ strings.add(WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED);
+ strings.add(CONNECTED_WORK_AND_PERSONAL_APPS_TITLE);
+ strings.add(CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA);
+ strings.add(ONLY_CONNECT_TRUSTED_APPS);
+ strings.add(HOW_TO_DISCONNECT_APPS);
+ strings.add(CONNECT_APPS_DIALOG_TITLE);
+ strings.add(CONNECT_APPS_DIALOG_SUMMARY);
+ strings.add(APP_CAN_ACCESS_PERSONAL_DATA);
+ strings.add(APP_CAN_ACCESS_PERSONAL_PERMISSIONS);
+ strings.add(INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT);
+ strings.add(INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT);
+ strings.add(WORK_PROFILE_MANAGED_BY);
+ strings.add(MANAGED_BY);
+ strings.add(WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING);
+ strings.add(DISABLED_BY_IT_ADMIN_TITLE);
+ strings.add(CONTACT_YOUR_IT_ADMIN);
+ strings.add(WORK_PROFILE_ADMIN_POLICIES_WARNING);
+ strings.add(USER_ADMIN_POLICIES_WARNING);
+ strings.add(DEVICE_ADMIN_POLICIES_WARNING);
+ strings.add(WORK_PROFILE_OFF_CONDITION_TITLE);
+ strings.add(MANAGED_PROFILE_SETTINGS_TITLE);
+ strings.add(WORK_PROFILE_CONTACT_SEARCH_TITLE);
+ strings.add(WORK_PROFILE_CONTACT_SEARCH_SUMMARY);
+ strings.add(CROSS_PROFILE_CALENDAR_TITLE);
+ strings.add(CROSS_PROFILE_CALENDAR_SUMMARY);
+ strings.add(ALWAYS_ON_VPN_PERSONAL_PROFILE);
+ strings.add(ALWAYS_ON_VPN_DEVICE);
+ strings.add(ALWAYS_ON_VPN_WORK_PROFILE);
+ strings.add(CA_CERTS_PERSONAL_PROFILE);
+ strings.add(CA_CERTS_WORK_PROFILE);
+ strings.add(CA_CERTS_DEVICE);
+ strings.add(ADMIN_CAN_LOCK_DEVICE);
+ strings.add(ADMIN_CAN_WIPE_DEVICE);
+ strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE);
+ strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE);
+ strings.add(DEVICE_MANAGED_WITHOUT_NAME);
+ strings.add(DEVICE_MANAGED_WITH_NAME);
+ strings.add(WORK_PROFILE_APP_SUBTEXT);
+ strings.add(PERSONAL_PROFILE_APP_SUBTEXT);
+ strings.add(FINGERPRINT_FOR_WORK);
+ strings.add(FACE_UNLOCK_DISABLED);
+ strings.add(FINGERPRINT_UNLOCK_DISABLED);
+ strings.add(FINGERPRINT_UNLOCK_DISABLED_EXPLANATION);
+ strings.add(PIN_RECENTLY_USED);
+ strings.add(PASSWORD_RECENTLY_USED);
+ strings.add(MANAGE_DEVICE_ADMIN_APPS);
+ strings.add(NUMBER_OF_DEVICE_ADMINS_NONE);
+ strings.add(NUMBER_OF_DEVICE_ADMINS);
+ strings.add(FORGOT_PASSWORD_TITLE);
+ strings.add(FORGOT_PASSWORD_TEXT);
+ strings.add(ERROR_MOVE_DEVICE_ADMIN);
+ strings.add(DEVICE_ADMIN_SETTINGS_TITLE);
+ strings.add(REMOVE_DEVICE_ADMIN);
+ strings.add(UNINSTALL_DEVICE_ADMIN);
+ strings.add(REMOVE_AND_UNINSTALL_DEVICE_ADMIN);
+ strings.add(SELECT_DEVICE_ADMIN_APPS);
+ strings.add(NO_DEVICE_ADMINS);
+ strings.add(ACTIVATE_DEVICE_ADMIN_APP);
+ strings.add(ACTIVATE_THIS_DEVICE_ADMIN_APP);
+ strings.add(ACTIVATE_DEVICE_ADMIN_APP_TITLE);
+ strings.add(NEW_DEVICE_ADMIN_WARNING);
+ strings.add(NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED);
+ strings.add(ACTIVE_DEVICE_ADMIN_WARNING);
+ strings.add(SET_PROFILE_OWNER_TITLE);
+ strings.add(SET_PROFILE_OWNER_DIALOG_TITLE);
+ strings.add(SET_PROFILE_OWNER_POSTSETUP_WARNING);
+ strings.add(OTHER_OPTIONS_DISABLED_BY_ADMIN);
+ strings.add(REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION);
+ strings.add(IT_ADMIN_POLICY_DISABLING_INFO_URL);
+ strings.add(SHARE_REMOTE_BUGREPORT_DIALOG_TITLE);
+ strings.add(SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT);
+ strings.add(SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT);
+ strings.add(SHARING_REMOTE_BUGREPORT_MESSAGE);
+ strings.add(MANAGED_DEVICE_INFO);
+ strings.add(MANAGED_DEVICE_INFO_SUMMARY);
+ strings.add(MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME);
+ strings.add(ENTERPRISE_PRIVACY_HEADER);
+ strings.add(INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE);
+ strings.add(CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE);
+ strings.add(YOUR_ACCESS_TO_THIS_DEVICE_TITLE);
+ strings.add(ADMIN_CAN_SEE_WORK_DATA_WARNING);
+ strings.add(ADMIN_CAN_SEE_APPS_WARNING);
+ strings.add(ADMIN_CAN_SEE_USAGE_WARNING);
+ strings.add(ADMIN_CAN_SEE_NETWORK_LOGS_WARNING);
+ strings.add(ADMIN_CAN_SEE_BUG_REPORT_WARNING);
+ strings.add(ADMIN_CAN_SEE_SECURITY_LOGS_WARNING);
+ strings.add(ADMIN_ACTION_NONE);
+ strings.add(ADMIN_ACTION_APPS_INSTALLED);
+ strings.add(ADMIN_ACTION_APPS_COUNT_ESTIMATED);
+ strings.add(ADMIN_ACTIONS_APPS_COUNT_MINIMUM);
+ strings.add(ADMIN_ACTION_ACCESS_LOCATION);
+ strings.add(ADMIN_ACTION_ACCESS_MICROPHONE);
+ strings.add(ADMIN_ACTION_ACCESS_CAMERA);
+ strings.add(ADMIN_ACTION_SET_DEFAULT_APPS);
+ strings.add(ADMIN_ACTIONS_APPS_COUNT);
+ strings.add(ADMIN_ACTION_SET_CURRENT_INPUT_METHOD);
+ strings.add(ADMIN_ACTION_SET_INPUT_METHOD_NAME);
+ strings.add(ADMIN_ACTION_SET_HTTP_PROXY);
+ strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY);
+ strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO);
+ strings.add(CONNECTED_APPS_SEARCH_KEYWORDS);
+ strings.add(WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS);
+ strings.add(ACCOUNTS_SEARCH_KEYWORDS);
+ strings.add(CONTROLLED_BY_ADMIN_SUMMARY);
+ strings.add(WORK_PROFILE_USER_LABEL);
+ strings.add(WORK_CATEGORY_HEADER);
+ strings.add(PERSONAL_CATEGORY_HEADER);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
* in the Launcher package.
*
* @hide
*/
public static final class Launcher {
- private Launcher(){}
+ private Launcher() {
+ }
private static final String PREFIX = "Launcher.";
@@ -576,6 +2016,7 @@
private SystemUi() {
}
+
private static final String PREFIX = "SystemUi.";
/**
@@ -649,9 +2090,9 @@
PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
/**
- * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
+ * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
* admin.
- */
+ */
public static final String QS_MSG_WORK_PROFILE_NETWORK =
PREFIX + "QS_MSG_WORK_PROFILE_NETWORK";
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 8c232c0..1f7ae4a 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -25,6 +25,7 @@
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.stats.devicepolicy.DevicePolicyEnums;
import java.util.Locale;
@@ -52,6 +53,7 @@
@SuppressLint("UseIcu")
@Nullable private final Locale mLocale;
private final boolean mDeviceOwnerCanGrantSensorsPermissions;
+ @NonNull private final PersistableBundle mAdminExtras;
private FullyManagedDeviceProvisioningParams(
@NonNull ComponentName deviceAdminComponentName,
@@ -60,7 +62,8 @@
@Nullable String timeZone,
long localTime,
@Nullable @SuppressLint("UseIcu") Locale locale,
- boolean deviceOwnerCanGrantSensorsPermissions) {
+ boolean deviceOwnerCanGrantSensorsPermissions,
+ @NonNull PersistableBundle adminExtras) {
this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
@@ -69,6 +72,7 @@
this.mLocale = locale;
this.mDeviceOwnerCanGrantSensorsPermissions =
deviceOwnerCanGrantSensorsPermissions;
+ this.mAdminExtras = adminExtras;
}
private FullyManagedDeviceProvisioningParams(
@@ -78,14 +82,16 @@
@Nullable String timeZone,
long localTime,
@Nullable String localeStr,
- boolean deviceOwnerCanGrantSensorsPermissions) {
+ boolean deviceOwnerCanGrantSensorsPermissions,
+ @Nullable PersistableBundle adminExtras) {
this(deviceAdminComponentName,
ownerName,
leaveAllSystemAppsEnabled,
timeZone,
localTime,
getLocale(localeStr),
- deviceOwnerCanGrantSensorsPermissions);
+ deviceOwnerCanGrantSensorsPermissions,
+ adminExtras);
}
@Nullable
@@ -151,6 +157,15 @@
}
/**
+ * Returns a copy of the admin extras bundle.
+ *
+ * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+ */
+ public @NonNull PersistableBundle getAdminExtras() {
+ return new PersistableBundle(mAdminExtras);
+ }
+
+ /**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
*
* @hide
@@ -188,6 +203,7 @@
@Nullable private Locale mLocale;
// Default to allowing control over sensor permission grants.
boolean mDeviceOwnerCanGrantSensorsPermissions = true;
+ @NonNull private PersistableBundle mAdminExtras;
/**
* Initialize a new {@link Builder} to construct a
@@ -262,6 +278,17 @@
}
/**
+ * Sets a {@link PersistableBundle} that contains admin-specific extras.
+ */
+ @NonNull
+ public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
+ mAdminExtras = adminExtras != null
+ ? new PersistableBundle(adminExtras)
+ : new PersistableBundle();
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}
*
* @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -275,7 +302,8 @@
mTimeZone,
mLocalTime,
mLocale,
- mDeviceOwnerCanGrantSensorsPermissions);
+ mDeviceOwnerCanGrantSensorsPermissions,
+ mAdminExtras);
}
}
@@ -298,6 +326,7 @@
+ ", mLocale=" + (mLocale == null ? "null" : mLocale)
+ ", mDeviceOwnerCanGrantSensorsPermissions="
+ mDeviceOwnerCanGrantSensorsPermissions
+ + ", mAdminExtras=" + mAdminExtras
+ '}';
}
@@ -310,6 +339,7 @@
dest.writeLong(mLocalTime);
dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
+ dest.writePersistableBundle(mAdminExtras);
}
@NonNull
@@ -324,6 +354,7 @@
long localtime = in.readLong();
String locale = in.readString();
boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
+ PersistableBundle adminExtras = in.readPersistableBundle();
return new FullyManagedDeviceProvisioningParams(
componentName,
@@ -332,7 +363,8 @@
timeZone,
localtime,
locale,
- deviceOwnerCanGrantSensorsPermissions);
+ deviceOwnerCanGrantSensorsPermissions,
+ adminExtras);
}
@Override
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index ccbef73..f91d60a 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -23,8 +23,10 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.stats.devicepolicy.DevicePolicyEnums;
/**
@@ -49,7 +51,7 @@
private final boolean mLeaveAllSystemAppsEnabled;
private final boolean mOrganizationOwnedProvisioning;
private final boolean mKeepAccountOnMigration;
-
+ @NonNull private final PersistableBundle mAdminExtras;
private ManagedProfileProvisioningParams(
@NonNull ComponentName profileAdminComponentName,
@@ -58,7 +60,8 @@
@Nullable Account accountToMigrate,
boolean leaveAllSystemAppsEnabled,
boolean organizationOwnedProvisioning,
- boolean keepAccountOnMigration) {
+ boolean keepAccountOnMigration,
+ @NonNull PersistableBundle adminExtras) {
this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mProfileName = profileName;
@@ -66,6 +69,7 @@
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
this.mKeepAccountOnMigration = keepAccountOnMigration;
+ this.mAdminExtras = adminExtras;
}
/**
@@ -124,6 +128,15 @@
}
/**
+ * Returns a copy of the admin extras bundle.
+ *
+ * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+ */
+ public @NonNull PersistableBundle getAdminExtras() {
+ return new PersistableBundle(mAdminExtras);
+ }
+
+ /**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
*
* @hide
@@ -160,6 +173,7 @@
private boolean mLeaveAllSystemAppsEnabled;
private boolean mOrganizationOwnedProvisioning;
private boolean mKeepingAccountOnMigration;
+ @Nullable private PersistableBundle mAdminExtras;
/**
* Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
@@ -235,6 +249,17 @@
}
/**
+ * Sets a {@link Bundle} that contains admin-specific extras.
+ */
+ @NonNull
+ public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
+ mAdminExtras = adminExtras != null
+ ? new PersistableBundle(adminExtras)
+ : new PersistableBundle();
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}.
*
* @return a new {@link ManagedProfileProvisioningParams} object.
@@ -248,7 +273,8 @@
mAccountToMigrate,
mLeaveAllSystemAppsEnabled,
mOrganizationOwnedProvisioning,
- mKeepingAccountOnMigration);
+ mKeepingAccountOnMigration,
+ mAdminExtras);
}
}
@@ -270,6 +296,7 @@
+ ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+ ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
+ ", mKeepAccountOnMigration=" + mKeepAccountOnMigration
+ + ", mAdminExtras=" + mAdminExtras
+ '}';
}
@@ -282,6 +309,7 @@
dest.writeBoolean(mLeaveAllSystemAppsEnabled);
dest.writeBoolean(mOrganizationOwnedProvisioning);
dest.writeBoolean(mKeepAccountOnMigration);
+ dest.writePersistableBundle(mAdminExtras);
}
public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
@@ -295,6 +323,7 @@
boolean leaveAllSystemAppsEnabled = in.readBoolean();
boolean organizationOwnedProvisioning = in.readBoolean();
boolean keepAccountMigrated = in.readBoolean();
+ PersistableBundle adminExtras = in.readPersistableBundle();
return new ManagedProfileProvisioningParams(
componentName,
@@ -303,7 +332,8 @@
account,
leaveAllSystemAppsEnabled,
organizationOwnedProvisioning,
- keepAccountMigrated);
+ keepAccountMigrated,
+ adminExtras);
}
@Override
diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java
index dba3628..0b1b166 100644
--- a/core/java/android/app/admin/ParcelableResource.java
+++ b/core/java/android/app/admin/ParcelableResource.java
@@ -175,7 +175,7 @@
* <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated
* drawable was not found or could not be loaded.</p>
*/
- @NonNull
+ @Nullable
public Drawable getDrawable(
Context context,
int density,
@@ -200,7 +200,7 @@
* <p>Returns the default string by calling {@code defaultStringLoader} if the updated
* string was not found or could not be loaded.</p>
*/
- @NonNull
+ @Nullable
public String getString(
Context context,
@NonNull Callable<String> defaultStringLoader) {
@@ -267,17 +267,11 @@
/**
* returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}.
*/
- @NonNull
+ @Nullable
public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) {
try {
Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
-
- Drawable drawable = defaultDrawableLoader.call();
- Objects.requireNonNull(drawable, "defaultDrawable can't be null");
-
- return drawable;
- } catch (NullPointerException rethrown) {
- throw rethrown;
+ return defaultDrawableLoader.call();
} catch (Exception e) {
throw new RuntimeException("Couldn't load default drawable: ", e);
}
@@ -286,17 +280,11 @@
/**
* returns the {@link String} loaded from calling {@code defaultStringLoader}.
*/
- @NonNull
+ @Nullable
public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) {
try {
Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
-
- String string = defaultStringLoader.call();
- Objects.requireNonNull(string, "defaultString can't be null");
-
- return string;
- } catch (NullPointerException rethrown) {
- throw rethrown;
+ return defaultStringLoader.call();
} catch (Exception e) {
throw new RuntimeException("Couldn't load default string: ", e);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0b8a8a2..a0864d6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@
import android.annotation.UiContext;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.BroadcastOptions;
import android.app.GameManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -2260,6 +2261,27 @@
}
/**
+ * Version of {@link #sendBroadcastMultiplePermissions(Intent, String[])} that allows you to
+ * specify the {@link android.app.BroadcastOptions}.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param receiverPermissions Array of names of permissions that a receiver must hold
+ * in order to receive your broadcast.
+ * If empty, no permissions are required.
+ * @param options Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ * @see #sendBroadcastMultiplePermissions(Intent, String[])
+ * @see android.app.BroadcastOptions
+ * @hide
+ */
+ @SystemApi
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions, @Nullable BroadcastOptions options) {
+ sendBroadcastMultiplePermissions(intent, receiverPermissions, options.toBundle());
+ }
+
+ /**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
* an array of required permissions to be enforced. This call is asynchronous; it returns
* immediately, and you will continue executing while the receivers are run. No results are
@@ -5636,6 +5658,15 @@
public static final String OVERLAY_SERVICE = "overlay";
/**
+ * Use with {@link #getSystemService(String)} to manage resources.
+ *
+ * @see #getSystemService(String)
+ * @see com.android.server.resources.ResourcesManagerService
+ * @hide
+ */
+ public static final String RESOURCES_SERVICE = "resources";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {android.os.IIdmap2} for managing idmap files (used by overlay
* packages).
@@ -6675,21 +6706,27 @@
@NonNull Configuration overrideConfiguration);
/**
- * Returns a new <code>Context</code> object from the current context but with resources
- * adjusted to match the metrics of <code>display</code>. Each call to this method
+ * Returns a new {@code Context} object from the current context but with resources
+ * adjusted to match the metrics of {@code display}. Each call to this method
* returns a new instance of a context object. Context objects are not shared; however,
* common state (such as the {@link ClassLoader} and other resources for the same
- * configuration) can be shared, so the <code>Context</code> itself is lightweight.
+ * configuration) can be shared, so the {@code Context} itself is lightweight.
+ *
+ * <p><b>Note:</b>
+ * This {@code Context} is <b>not</b> expected to be updated with new configuration if the
+ * underlying display configuration changes and the cached {@code Resources} it returns
+ * could be stale. It is suggested to use
+ * {@link android.hardware.display.DisplayManager.DisplayListener} to listen for
+ * changes and re-create an instance if necessary. </p>
* <p>
+ * This {@code Context} is <b>not</b> a UI context, do not use it to access UI components
+ * or obtain a {@link WindowManager} instance.
+ * </p><p>
* To obtain an instance of {@link WindowManager} configured to show windows on the given
* display, call {@link #createWindowContext(int, Bundle)} on the returned display context,
* then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the
* returned window context.
- * <p>
- * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI
- * context. Do not access UI components or obtain a {@link WindowManager} from the context
- * created by <code>createDisplayContext(Display)</code>.
- *
+ * </p>
* @param display The display to which the current context's resources are adjusted.
*
* @return A context for the display.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fb186fd..3e527f8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3986,7 +3986,7 @@
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
@@ -6196,6 +6196,8 @@
*
* @hide
*/
+ @SystemApi
+ @SuppressLint("ActionValue")
public static final String EXTRA_USER_HANDLE =
"android.intent.extra.user_handle";
@@ -7059,6 +7061,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
/**
* If set, the broadcast will never go to manifest receivers in background (cached
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 1e88758..94f0561 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -282,7 +282,8 @@
final boolean isManagedProfile =
mUserManager.isManagedProfile(userHandle.getIdentifier());
if (isManagedProfile) {
- return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+ return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
+ userHandle, /* density= */ 0);
} else {
return UserIcons.getDefaultUserIcon(
mResources, UserHandle.USER_SYSTEM, true /* light */);
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 9735f81..410e106 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
/**
* Basic information about a package as specified in its manifest.
@@ -80,10 +80,10 @@
/**
* Specifies the recommended install location. Can be one of
- * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
- * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
- * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
- * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
+ * {@link InstallLocationUtils#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+ * {@link InstallLocationUtils#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+ * {@link InstallLocationUtils#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+ * or {@link InstallLocationUtils#RECOMMEND_FAILED_INVALID_APK} for parse errors.
*/
public int recommendedInstallLocation;
public int installLocation;
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 6fd2d05..7a5ac8e 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -28,6 +28,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -438,6 +439,12 @@
}
}
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "debugName=" + getDebugName());
+ pw.println(prefix + "assetPath=" + getAssetPath());
+ }
+
private static native long nativeLoad(@FormatType int format, @NonNull String path,
@PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
private static native long nativeLoadEmpty(@PropertyFlags int flags,
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index bfd9fd0..a05f5c9 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -43,6 +43,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1531,6 +1532,15 @@
}
}
+ synchronized void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "apkAssets=");
+ for (int i = 0; i < mApkAssets.length; i++) {
+ pw.println(prefix + i);
+ mApkAssets[i].dump(pw, prefix + " ");
+ }
+ }
+
// AssetManager setup native methods.
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
diff --git a/core/java/android/content/res/IResourcesManager.aidl b/core/java/android/content/res/IResourcesManager.aidl
new file mode 100644
index 0000000..d137378
--- /dev/null
+++ b/core/java/android/content/res/IResourcesManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.content.res;
+
+import android.os.RemoteCallback;
+
+/**
+ * Api for getting information about resources.
+ *
+ * {@hide}
+ */
+interface IResourcesManager {
+ boolean dumpResources(in String process,
+ in ParcelFileDescriptor fd,
+ in RemoteCallback finishCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 5fd0d84..ebef053 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -53,6 +53,7 @@
import android.graphics.drawable.DrawableInflater;
import android.os.Build;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -78,11 +79,15 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -172,6 +177,11 @@
private int mBaseApkAssetsSize;
+ /** @hide */
+ private static Set<Resources> sResourcesHistory = Collections.synchronizedSet(
+ Collections.newSetFromMap(
+ new WeakHashMap<>()));
+
/**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
@@ -318,6 +328,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Resources(@Nullable ClassLoader classLoader) {
mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
+ sResourcesHistory.add(this);
}
/**
@@ -2649,4 +2660,29 @@
}
}
}
+
+ /** @hide */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "resourcesImpl");
+ mResourcesImpl.dump(pw, prefix + " ");
+ }
+
+ /** @hide */
+ public static void dumpHistory(PrintWriter pw, String prefix) {
+ pw.println(prefix + "history");
+ // Putting into a map keyed on the apk assets to deduplicate resources that are different
+ // objects but ultimately represent the same assets
+ Map<List<ApkAssets>, Resources> history = new ArrayMap<>();
+ for (Resources r : sResourcesHistory) {
+ history.put(Arrays.asList(r.mResourcesImpl.mAssets.getApkAssets()), r);
+ }
+ int i = 0;
+ for (Resources r : history.values()) {
+ if (r != null) {
+ pw.println(prefix + i++);
+ r.dump(pw, prefix + " ");
+ }
+ }
+ }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 4d850b0c..ff07291 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -61,6 +61,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Locale;
@@ -1271,6 +1272,12 @@
NativeAllocationRegistry.createMalloced(ResourcesImpl.class.getClassLoader(),
AssetManager.getThemeFreeFunction());
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "class=" + getClass());
+ pw.println(prefix + "assets");
+ mAssets.dump(pw, prefix + " ");
+ }
+
public class ThemeImpl {
/**
* Unique key for the series of styles applied to this theme.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f55c415..223b8cc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -475,7 +475,7 @@
private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
@NonNull
- final NavigationBarController mNavigationBarController =
+ private final NavigationBarController mNavigationBarController =
new NavigationBarController(this);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1504,7 +1504,7 @@
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
-
+ mNavigationBarController.onSoftInputWindowCreated(mWindow);
{
final Window window = mWindow.getWindow();
{
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 508172d..e5c22e4 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -65,6 +66,9 @@
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
}
+ default void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ }
+
default void onViewInitialized() {
}
@@ -78,9 +82,6 @@
boolean shouldShowImeSwitcherWhenImeIsShown) {
}
- default void onSystemBarAppearanceChanged(@Appearance int appearance) {
- }
-
default String toDebugString() {
return "No-op implementation";
}
@@ -101,6 +102,10 @@
mImpl.updateTouchableInsets(originalInsets, dest);
}
+ void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ mImpl.onSoftInputWindowCreated(softInputWindow);
+ }
+
void onViewInitialized() {
mImpl.onViewInitialized();
}
@@ -117,15 +122,11 @@
mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown);
}
- void onSystemBarAppearanceChanged(@Appearance int appearance) {
- mImpl.onSystemBarAppearanceChanged(appearance);
- }
-
String toDebugString() {
return mImpl.toDebugString();
}
- private static final class Impl implements Callback {
+ private static final class Impl implements Callback, Window.DecorCallback {
private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
// Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE
@@ -158,6 +159,8 @@
@Nullable
private ValueAnimator mTintAnimator;
+ private boolean mDrawLegacyNavigationBarBackground;
+
Impl(@NonNull InputMethodService inputMethodService) {
mService = inputMethodService;
}
@@ -226,9 +229,14 @@
mLastInsets = systemInsets;
}
- mNavigationBarFrame.setBackground(null);
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
- setIconTintInternal(calculateTargetDarkIntensity(mAppearance));
+ setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground));
}
private void uninstallNavigationBarFrameIfNecessary() {
@@ -362,6 +370,13 @@
}
@Override
+ public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) {
+ final Window window = softInputWindow.getWindow();
+ mAppearance = window.getSystemBarAppearance();
+ window.setDecorCallback(this);
+ }
+
+ @Override
public void onViewInitialized() {
if (mDestroyed) {
return;
@@ -471,7 +486,8 @@
return;
}
- final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance);
+ final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground);
if (mTintAnimator != null) {
mTintAnimator.cancel();
@@ -499,18 +515,41 @@
}
@FloatRange(from = 0.0f, to = 1.0f)
- private static float calculateTargetDarkIntensity(@Appearance int appearance) {
- final boolean lightNavBar = (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
+ private static float calculateTargetDarkIntensity(@Appearance int appearance,
+ boolean drawLegacyNavigationBarBackground) {
+ final boolean lightNavBar = !drawLegacyNavigationBarBackground
+ && (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
return lightNavBar ? 1.0f : 0.0f;
}
@Override
+ public boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground;
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
+ scheduleRelayout();
+ onSystemBarAppearanceChanged(mAppearance);
+ }
+ return drawLegacyNavigationBarBackground;
+ }
+
+ @Override
public String toDebugString() {
return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
+ " mNavigationBarFrame=" + mNavigationBarFrame
+ " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown
+ " mAppearance=0x" + Integer.toHexString(mAppearance)
+ " mDarkIntensity=" + mDarkIntensity
+ + " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground
+ "}";
}
}
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0893d2a..5704dac 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -31,7 +31,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.WindowInsetsController;
import android.view.WindowManager;
import java.lang.annotation.Retention;
@@ -264,11 +263,6 @@
}
}
- @Override
- public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
- mService.mNavigationBarController.onSystemBarAppearanceChanged(appearance);
- }
-
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
mBounds.dumpDebug(proto, BOUNDS);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index c936bfa..9122adf 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -523,9 +523,11 @@
*
* @param subId the subscriber to get the subscription plans for.
* @param callingPackage the name of the package making the call.
+ * @return the active {@link SubscriptionPlan}s for the given subscription id, or
+ * {@code null} if not found.
* @hide
*/
- @NonNull
+ @Nullable
public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
try {
return mService.getSubscriptionPlans(subId, callingPackage);
@@ -538,7 +540,7 @@
* Get subscription plan for the given networkTemplate.
*
* @param template the networkTemplate to get the subscription plan for.
- * @return the active {@link SubscriptionPlan} for the given template, or
+ * @return the active {@link SubscriptionPlan}s for the given template, or
* {@code null} if not found.
* @hide
*/
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index df5b7bc..4fe6524 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -21,15 +21,16 @@
import android.os.ParcelDuration;
import android.os.PowerSaveState;
import android.os.WorkSource;
+import android.os.IWakeLockCallback;
/** @hide */
interface IPowerManager
{
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
- String historyTag, int displayId);
+ String historyTag, int displayId, IWakeLockCallback callback);
void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
- int uidtoblame, int displayId);
+ int uidtoblame, int displayId, IWakeLockCallback callback);
@UnsupportedAppUsage
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
@@ -40,6 +41,7 @@
boolean setPowerModeChecked(int mode, boolean enabled);
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag);
+ void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback);
boolean isWakeLockLevelSupported(int level);
void userActivity(int displayId, long time, int event, int flags);
diff --git a/core/java/android/os/IWakeLockCallback.aidl b/core/java/android/os/IWakeLockCallback.aidl
new file mode 100644
index 0000000..89615d2
--- /dev/null
+++ b/core/java/android/os/IWakeLockCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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;
+
+/**
+ * @hide
+ */
+oneway interface IWakeLockCallback {
+ oneway void onStateChanged(boolean enabled);
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 5bd8588..315eef7 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2770,6 +2770,23 @@
public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2;
/**
+ * A listener interface to get notified when the wakelock is enabled/disabled.
+ */
+ public interface WakeLockStateListener {
+ /**
+ * Frameworks could disable the wakelock because either device's power allowlist has
+ * changed, or the app's wakelock has exceeded its quota, or the app goes into cached
+ * state.
+ * <p>
+ * This callback is called whenever the wakelock's state has changed.
+ * </p>
+ *
+ * @param enabled true is enabled, false is disabled.
+ */
+ void onStateChanged(boolean enabled);
+ }
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
@@ -2800,6 +2817,8 @@
private String mHistoryTag;
private final String mTraceName;
private final int mDisplayId;
+ private WakeLockStateListener mListener;
+ private IWakeLockCallback mCallback;
private final Runnable mReleaser = () -> release(RELEASE_FLAG_TIMEOUT);
@@ -2890,7 +2909,7 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
- mHistoryTag, mDisplayId);
+ mHistoryTag, mDisplayId, mCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3083,6 +3102,45 @@
}
};
}
+
+ /**
+ * Set the listener to get notified when the wakelock is enabled/disabled.
+ *
+ * @param executor {@link Executor} to handle listener callback.
+ * @param listener listener to be added, set the listener to null to cancel a listener.
+ */
+ public void setStateListener(@NonNull @CallbackExecutor Executor executor,
+ @Nullable WakeLockStateListener listener) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
+ synchronized (mToken) {
+ if (listener != mListener) {
+ mListener = listener;
+ if (listener != null) {
+ mCallback = new IWakeLockCallback.Stub() {
+ public void onStateChanged(boolean enabled) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ listener.onStateChanged(enabled);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ } else {
+ mCallback = null;
+ }
+ if (mHeld) {
+ try {
+ mService.updateWakeLockCallback(mToken, mCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 9598410..21c6487 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -410,9 +410,9 @@
*
* <p>The waveform will start the first transition from the vibrator off state, with the
* resonant frequency by default. To provide an initial state, use
- * {@link #startWaveform(VibrationParameter)}.
+ * {@link #startWaveform(VibrationEffect.VibrationParameter)}.
*
- * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform() {
@@ -421,14 +421,16 @@
/**
* Start building a waveform vibration with an initial state specified by a
- * {@link VibrationParameter}.
+ * {@link VibrationEffect.VibrationParameter}.
*
* <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
* control over vibration amplitude and frequency via smooth transitions between values.
*
- * @param initialParameter The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration.
+ * @param initialParameter The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration.
* @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ *
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) {
@@ -439,17 +441,19 @@
/**
* Start building a waveform vibration with an initial state specified by two
- * {@link VibrationParameter VibrationParameters}.
+ * {@link VibrationEffect.VibrationParameter VibrationParameters}.
*
* <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
* control over vibration amplitude and frequency via smooth transitions between values.
*
- * @param initialParameter1 The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration.
- * @param initialParameter2 The initial {@link VibrationParameter} value to be applied at the
- * beginning of the vibration, must be a different type of parameter
- * than the one specified by the first argument.
+ * @param initialParameter1 The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration.
+ * @param initialParameter2 The initial {@link VibrationEffect.VibrationParameter} value to be
+ * applied at the beginning of the vibration, must be a different type
+ * of parameter than the one specified by the first argument.
* @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
+ *
+ * @see VibrationEffect.WaveformBuilder
*/
@NonNull
public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1,
@@ -805,7 +809,46 @@
}
/**
- * A composition of haptic primitives that, when combined, create a single haptic effect.
+ * A composition of haptic elements that are combined to be playable as a single
+ * {@link VibrationEffect}.
+ *
+ * <p>The haptic primitives are available as {@code Composition.PRIMITIVE_*} constants and
+ * can be added to a composition to create a custom vibration effect. Here is an example of an
+ * effect that grows in intensity and then dies off, with a longer rising portion for emphasis
+ * and an extra tick 100ms after:
+ *
+ * <code>
+ * VibrationEffect effect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
+ * .compose();
+ * </code>
+ *
+ * <p>Composition elements can also be {@link VibrationEffect} instances, including other
+ * compositions, and off durations, which are periods of time when the vibrator will be
+ * turned off. Here is an example of a composition that "warms up" with a light tap,
+ * a stronger double tap, then repeats a vibration pattern indefinitely:
+ *
+ * <code>
+ * VibrationEffect repeatingEffect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ * .addOffDuration(Duration.ofMillis(10))
+ * .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
+ * .addOffDuration(Duration.ofMillis(50))
+ * .addEffect(VibrationEffect.createWaveform(pattern, repeatIndex))
+ * .compose();
+ * </code>
+ *
+ * <p>When choosing to play a composed effect, you should check that individual components are
+ * supported by the device by using the appropriate vibrator method:
+ *
+ * <ul>
+ * <li>Primitive support can be checked using {@link Vibrator#arePrimitivesSupported}.
+ * <li>Effect support can be checked using {@link Vibrator#areEffectsSupported}.
+ * <li>Amplitude control for one-shot and waveforms with amplitude values can be checked
+ * using {@link Vibrator#hasAmplitudeControl}.
+ * </ul>
*
* @see VibrationEffect#startComposition()
*/
@@ -1091,16 +1134,77 @@
* A builder for waveform haptic effects.
*
* <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration
- * parameters. These parameters can be the vibration amplitude or frequency, for example.
+ * parameters. These parameters can be the vibration amplitude, frequency, or both.
+ *
+ * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms
+ * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms:
+ *
+ * <code>
+ * import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
+ * import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
+ *
+ * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
+ * .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+ * .addSustain(Duration.ofMillis(200))
+ * .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+ * .build();
+ * </code>
+ *
+ * <p>The initial state of the waveform can be set via
+ * {@link VibrationEffect#startWaveform(VibrationParameter)} or
+ * {@link VibrationEffect#startWaveform(VibrationParameter, VibrationParameter)}. If the initial
+ * parameters are not set then the {@link WaveformBuilder} will start with the vibrator off,
+ * represented by zero amplitude, at the vibrator's resonant frequency.
+ *
+ * <p>Repeating waveforms can be created by building the repeating block separately and adding
+ * it to the end of a composition with
+ * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}:
*
* <p>Note that physical vibration actuators have different reaction times for changing
* amplitude and frequency. Durations specified here represent a timeline for the target
* parameters, and quality of effects may be improved if the durations allow time for a
* transition to be smoothly applied.
*
- * <p>Repeating waveforms can be built by constructing the repeating block separately and adding
- * it to the end of a composition using
- * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}.
+ * <p>The following example illustrates both an initial state and a repeating section, using
+ * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a
+ * repeated beating effect with a rise that stretches out and a sharp finish.
+ *
+ * <code>
+ * VibrationEffect patternToBeRepeated = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ * .addSustain(Duration.ofMillis(10))
+ * .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
+ * .addSustain(Duration.ofMillis(30))
+ * .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
+ * .addSustain(Duration.ofMillis(50))
+ * .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
+ * .build();
+ *
+ * VibrationEffect effect = VibrationEffect.startComposition()
+ * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ * .addOffDuration(Duration.ofMillis(20))
+ * .repeatEffectIndefinitely(patternToBeRepeated)
+ * .compose();
+ * </code>
+ *
+ * <p>The amplitude step waveforms that can be created via
+ * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with
+ * {@link WaveformBuilder} by adding zero duration transitions:
+ *
+ * <code>
+ * // These two effects are the same
+ * VibrationEffect waveform = VibrationEffect.createWaveform(
+ * new long[] { 10, 20, 30 }, // timings in milliseconds
+ * new int[] { 51, 102, 204 }, // amplitudes in [0,255]
+ * -1); // repeat index
+ *
+ * VibrationEffect sameWaveform = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ * .addSustain(Duration.ofMillis(10))
+ * .addTransition(Duration.ZERO, targetAmplitude(0.4f))
+ * .addSustain(Duration.ofMillis(20))
+ * .addTransition(Duration.ZERO, targetAmplitude(0.8f))
+ * .addSustain(Duration.ofMillis(30))
+ * .build();
+ * </code>
*
* @see VibrationEffect#startWaveform
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 14055ac..4e9aec7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2436,6 +2436,23 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING";
+ /**
+ * Activity Action: Show a screen of bedtime settings, which is provided by the wellbeing app.
+ * <p>
+ * The handler of this intent action may not exist.
+ * <p>
+ * To start an activity with this intent, apps should set the wellbeing package explicitly in
+ * the intent together with this action. The wellbeing package is defined in
+ * {@code com.android.internal.R.string.config_defaultWellbeingPackage}.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -10048,6 +10065,13 @@
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
/**
+ * The duration of timeout, in milliseconds, to switch from a non-primary user to the
+ * primary user when the device is docked.
+ * @hide
+ */
+ public static final String TIMEOUT_TO_USER_ZERO = "timeout_to_user_zero";
+
+ /**
* Backup manager behavioral parameters.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -10281,6 +10305,15 @@
public static final String NEARBY_SHARING_SLICE_URI = "nearby_sharing_slice_uri";
/**
+ * Current provider of Fast Pair saved devices page.
+ * Default value in @string/config_defaultNearbyFastPairSettingsDevicesComponent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String NEARBY_FAST_PAIR_SETTINGS_DEVICES_COMPONENT =
+ "nearby_fast_pair_settings_devices_component";
+
+ /**
* Controls whether aware is enabled.
* @hide
*/
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index 870a7e3..9df8358 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -16,9 +16,11 @@
package android.service.games;
+import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.IGameManagerService;
@@ -173,6 +175,7 @@
*
* @param taskId The taskId of the game.
*/
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
public final void createGameSession(@IntRange(from = 0) int taskId) {
if (mGameServiceController == null) {
throw new IllegalStateException("Can not call before connected()");
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 039b50a..8401b7c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -295,6 +295,9 @@
private OnWindowDismissedCallback mOnWindowDismissedCallback;
private OnWindowSwipeDismissedCallback mOnWindowSwipeDismissedCallback;
private WindowControllerCallback mWindowControllerCallback;
+ @WindowInsetsController.Appearance
+ private int mSystemBarAppearance;
+ private DecorCallback mDecorCallback;
private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener;
private Rect mRestrictedCaptionAreaRect;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -607,17 +610,6 @@
* @param hasCapture True if the window has pointer capture.
*/
default public void onPointerCaptureChanged(boolean hasCapture) { };
-
- /**
- * Called from
- * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}.
- *
- * @param appearance The newly applied appearance.
- * @hide
- */
- default void onSystemBarAppearanceChanged(
- @WindowInsetsController.Appearance int appearance) {
- }
}
/** @hide */
@@ -672,6 +664,35 @@
void updateNavigationBarColor(int color);
}
+ /** @hide */
+ public interface DecorCallback {
+ /**
+ * Called from
+ * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}.
+ *
+ * @param appearance The newly applied appearance.
+ */
+ void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance);
+
+ /**
+ * Called from
+ * {@link com.android.internal.policy.DecorView#updateColorViews(WindowInsets, boolean)}
+ * when {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground} is
+ * being updated.
+ *
+ * @param drawLegacyNavigationBarBackground the new value that is being set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground}.
+ * @return The value to be set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackgroundHandled}
+ * on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code true} to tell that the Window can render the legacy navigation bar
+ * background on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code false} to let {@link com.android.internal.policy.DecorView} handle it.
+ */
+ boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground);
+ }
+
/**
* Callback for clients that want to be aware of where caption draws content.
*/
@@ -996,6 +1017,36 @@
return mWindowControllerCallback;
}
+ /** @hide */
+ public final void setDecorCallback(DecorCallback decorCallback) {
+ mDecorCallback = decorCallback;
+ }
+
+ /** @hide */
+ @WindowInsetsController.Appearance
+ public final int getSystemBarAppearance() {
+ return mSystemBarAppearance;
+ }
+
+ /** @hide */
+ public final void dispatchOnSystemBarAppearanceChanged(
+ @WindowInsetsController.Appearance int appearance) {
+ mSystemBarAppearance = appearance;
+ if (mDecorCallback != null) {
+ mDecorCallback.onSystemBarAppearanceChanged(appearance);
+ }
+ }
+
+ /** @hide */
+ public final boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDecorCallback == null) {
+ return false;
+ }
+ return mDecorCallback.onDrawLegacyNavigationBarBackgroundChanged(
+ drawLegacyNavigationBarBackground);
+ }
+
/**
* Set a callback for changes of area where caption will draw its content.
*
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 115e9e8..02c8945 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -163,10 +163,5 @@
public void onPointerCaptureChanged(boolean hasCapture) {
mWrapped.onPointerCaptureChanged(hasCapture);
}
-
- @Override
- public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
- mWrapped.onSystemBarAppearanceChanged(appearance);
- }
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/InstallLocationUtils.java
similarity index 87%
rename from core/java/com/android/internal/content/PackageHelper.java
rename to core/java/com/android/internal/content/InstallLocationUtils.java
index c2f2052..c456cf3 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/InstallLocationUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.content;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
import android.content.Context;
@@ -53,7 +54,7 @@
* and media container service transports.
* Some utility methods to invoke StorageManagerService api.
*/
-public class PackageHelper {
+public class InstallLocationUtils {
public static final int RECOMMEND_INSTALL_INTERNAL = 1;
public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
public static final int RECOMMEND_INSTALL_EPHEMERAL = 3;
@@ -89,9 +90,13 @@
*/
public static abstract class TestableInterface {
abstract public StorageManager getStorageManager(Context context);
+
abstract public boolean getForceAllowOnExternalSetting(Context context);
+
abstract public boolean getAllow3rdPartyOnInternalConfig(Context context);
+
abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName);
+
abstract public File getDataDirectory();
}
@@ -150,11 +155,11 @@
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual volume to install the app. Only considers
- * internal and private volumes, and prefers to keep an existing package on
+ * internal and private volumes, and prefers to keep an existing package onocation
* its current volume.
*
* @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null}
- * for internal storage.
+ * for internal storage.
*/
public static String resolveInstallVolume(Context context, SessionParams params)
throws IOException {
@@ -316,21 +321,6 @@
&& params.sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
}
- @Deprecated
- public static int resolveInstallLocation(Context context, String packageName,
- int installLocation, long sizeBytes, int installFlags) {
- final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);
- params.appPackageName = packageName;
- params.installLocation = installLocation;
- params.sizeBytes = sizeBytes;
- params.installFlags = installFlags;
- try {
- return resolveInstallLocation(context, params);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual location to install the app.
@@ -393,24 +383,24 @@
// and will fall through to return INSUFFICIENT_STORAGE
if (fitsOnInternal) {
return (ephemeral)
- ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL
- : PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ ? InstallLocationUtils.RECOMMEND_INSTALL_EPHEMERAL
+ : InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
}
} else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
if (fitsOnExternal) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
}
if (checkBoth) {
if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else if (fitsOnExternal) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
}
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ return InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
@Deprecated
@@ -476,4 +466,48 @@
return 0;
}
}
+
+ public static int installLocationPolicy(int installLocation, int recommendedInstallLocation,
+ int installFlags, boolean installedPkgIsSystem, boolean installedPackageOnExternal) {
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) {
+ // Invalid install. Return error code
+ return RECOMMEND_FAILED_ALREADY_EXISTS;
+ }
+ // Check for updated system application.
+ if (installedPkgIsSystem) {
+ return RECOMMEND_INSTALL_INTERNAL;
+ }
+ // If current upgrade specifies particular preference
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ // Application explicitly specified internal.
+ return RECOMMEND_INSTALL_INTERNAL;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ // App explicitly prefers external. Let policy decide
+ return recommendedInstallLocation;
+ } else {
+ // Prefer previous location
+ if (installedPackageOnExternal) {
+ return RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return RECOMMEND_INSTALL_INTERNAL;
+ }
+ }
+
+ public static int getInstallationErrorCode(int loc) {
+ if (loc == RECOMMEND_FAILED_INVALID_LOCATION) {
+ return PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (loc == RECOMMEND_FAILED_ALREADY_EXISTS) {
+ return PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+ } else if (loc == RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ } else if (loc == RECOMMEND_FAILED_INVALID_APK) {
+ return PackageManager.INSTALL_FAILED_INVALID_APK;
+ } else if (loc == RECOMMEND_FAILED_INVALID_URI) {
+ return PackageManager.INSTALL_FAILED_INVALID_URI;
+ } else if (loc == RECOMMEND_MEDIA_UNAVAILABLE) {
+ return PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+ } else {
+ return INSTALL_SUCCEEDED;
+ }
+ }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a50282e..2925341 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -285,6 +285,7 @@
private Insets mBackgroundInsets = Insets.NONE;
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
+ private boolean mDrawLegacyNavigationBarBackgroundHandled;
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
@@ -1035,10 +1036,7 @@
public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
updateColorViews(null /* insets */, true /* animate */);
if (mWindow != null) {
- final Window.Callback callback = mWindow.getCallback();
- if (callback != null) {
- callback.onSystemBarAppearanceChanged(appearance);
- }
+ mWindow.dispatchOnSystemBarAppearanceChanged(appearance);
}
}
@@ -1174,6 +1172,9 @@
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackgroundHandled =
+ mWindow.onDrawLegacyNavigationBarBackgroundChanged(
+ mDrawLegacyNavigationBarBackground);
if (viewRoot != null) {
viewRoot.requestInvalidateRootRenderNode();
}
@@ -1266,7 +1267,7 @@
}
}
- if (forceConsumingNavBar) {
+ if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) {
mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
} else {
mBackgroundInsets = Insets.NONE;
@@ -2491,7 +2492,7 @@
}
private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
- if (!mDrawLegacyNavigationBarBackground) {
+ if (!mDrawLegacyNavigationBarBackground || mDrawLegacyNavigationBarBackgroundHandled) {
return;
}
View v = mNavigationColorViewState.view;
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index faea7706e..ece6f2f3 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -29,7 +29,6 @@
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
-import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -915,7 +914,7 @@
* animation.
*/
public HardwareBuffer createCrossProfileAppsThumbnail(
- @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ Drawable thumbnailDrawable, Rect frame) {
final int width = frame.width();
final int height = frame.height();
@@ -924,14 +923,13 @@
canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
- final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
- drawable.setBounds(
+ thumbnailDrawable.setBounds(
(width - thumbnailSize) / 2,
(height - thumbnailSize) / 2,
(width + thumbnailSize) / 2,
(height + thumbnailSize) / 2);
- drawable.setTint(mContext.getColor(android.R.color.white));
- drawable.draw(canvas);
+ thumbnailDrawable.setTint(mContext.getColor(android.R.color.white));
+ thumbnailDrawable.draw(canvas);
picture.endRecording();
return Bitmap.createBitmap(picture).getHardwareBuffer();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b1ad3ca..9bcd7d2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1602,7 +1602,7 @@
android:protectionLevel="normal"
android:permissionFlags="removed"/>
- <!-- @hide We need to keep this around for backwards compatibility -->
+ <!-- @SystemApi @hide We need to keep this around for backwards compatibility -->
<permission android:name="android.permission.WRITE_SMS"
android:protectionLevel="normal"
android:permissionFlags="removed"/>
@@ -1680,7 +1680,7 @@
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
+ <!-- @SystemApi Allows an application to monitor incoming Bluetooth MAP messages, to record
or perform processing on them. -->
<!-- @hide -->
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
@@ -4627,6 +4627,13 @@
<permission android:name="android.permission.READ_FRAME_BUFFER"
android:protectionLevel="signature|recents" />
+ <!-- @SystemApi Allows an application to change the touch mode state.
+ Without this permission, an app can only change the touch mode
+ if it currently has focus.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
@@ -6754,6 +6761,10 @@
android:resource="@xml/autofill_compat_accessibility_service" />
</service>
+ <service android:name="com.google.android.startop.iorap.IorapForwardingService$IorapdJobServiceProxy"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service android:name="com.android.server.blob.BlobStoreIdleJobService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
diff --git a/core/res/res/drawable-car/car_checkbox.xml b/core/res/res/drawable-car/car_checkbox.xml
deleted file mode 100644
index 083a7aa..0000000
--- a/core/res/res/drawable-car/car_checkbox.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:width="@*android:dimen/car_primary_icon_size"
- android:height="@*android:dimen/car_primary_icon_size"
- android:drawable="@drawable/btn_check_material_anim"/>
- <item
- android:width="@*android:dimen/car_primary_icon_size"
- android:height="@*android:dimen/car_primary_icon_size"
- android:drawable="@drawable/car_checkbox_background"/>
-</layer-list>
diff --git a/core/res/res/drawable-car/car_checkbox_background.xml b/core/res/res/drawable-car/car_checkbox_background.xml
deleted file mode 100644
index 69dcdbb..0000000
--- a/core/res/res/drawable-car/car_checkbox_background.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2021 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_pressed="true">
- <shape android:shape="rectangle">
- <solid android:color="#8A0041BE" />
- <stroke android:width="4dp" android:color="#0041BE" />
- </shape>
- </item>
- <item android:state_focused="true">
- <shape android:shape="rectangle">
- <solid android:color="#3D0059B3" />
- <stroke android:width="8dp" android:color="#0059B3" />
- </shape>
- </item>
-</selector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1997e91..73568c1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4978,6 +4978,10 @@
<!-- URI used for Nearby Share SliceProvider scanning. -->
<string translatable="false" name="config_defaultNearbySharingSliceUri"></string>
+ <!-- Component name that accepts settings intents for saved devices.
+ Used by FastPairSettingsFragment. -->
+ <string translatable="false" name="config_defaultNearbyFastPairSettingsDevicesComponent"></string>
+
<!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
check after reboot or airplane mode toggling -->
<bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
@@ -5644,4 +5648,13 @@
<!-- The amount of time after becoming non-interactive (in ms) after which
Low Power Standby can activate. -->
<integer name="config_lowPowerStandbyNonInteractiveTimeout">5000</integer>
+
+
+ <!-- Mapping to select an Intent.EXTRA_DOCK_STATE value from extcon state
+ key-value pairs. Each entry is evaluated in order and is of the form:
+ "[EXTRA_DOCK_STATE value],key1=value1,key2=value2[,...]"
+ An entry with no key-value pairs is valid and can be used as a wildcard.
+ -->
+ <string-array name="config_dockExtconStateMapping">
+ </string-array>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1884467..94043ed 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4100,6 +4100,7 @@
<java-symbol type="layout" name="chooser_action_button" />
<java-symbol type="dimen" name="chooser_action_button_icon_size" />
<java-symbol type="string" name="config_defaultNearbySharingComponent" />
+ <java-symbol type="string" name="config_defaultNearbyFastPairSettingsDevicesComponent" />
<java-symbol type="bool" name="config_disable_all_cb_messages" />
<java-symbol type="drawable" name="ic_close" />
@@ -4678,6 +4679,8 @@
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
+ <java-symbol type="array" name="config_dockExtconStateMapping" />
+
<java-symbol type="string" name="notification_channel_abusive_bg_apps"/>
<java-symbol type="string" name="notification_title_abusive_bg_apps"/>
<java-symbol type="string" name="notification_content_abusive_bg_apps"/>
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index b2c4274..5c9044c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -646,6 +646,10 @@
}
@Override
+ public void dumpResources(ParcelFileDescriptor fd, RemoteCallback finishCallback) {
+ }
+
+ @Override
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
}
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index b66642c..fa657f7 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -71,6 +71,8 @@
private Resources mResources;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Drawable mDrawable;
+ @Mock
+ private PackageManager mPackageManager;
private CrossProfileApps mCrossProfileApps;
@Before
@@ -87,6 +89,7 @@
Context.DEVICE_POLICY_SERVICE);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
mDevicePolicyManager);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
}
@Before
@@ -131,7 +134,8 @@
setValidTargetProfile(MANAGED_PROFILE);
mCrossProfileApps.getProfileSwitchingIconDrawable(MANAGED_PROFILE);
- verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
+ verify(mPackageManager).getUserBadgeForDensityNoBackground(
+ MANAGED_PROFILE, /* density= */0);
}
@Test
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java
similarity index 85%
rename from core/tests/coretests/src/android/content/pm/PackageHelperTests.java
rename to core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java
index 947da0b..0629a99 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java
@@ -25,7 +25,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import org.mockito.Mockito;
@@ -36,10 +36,9 @@
import java.util.UUID;
@Presubmit
-public class PackageHelperTests extends AndroidTestCase {
+public class InstallLocationUtilsTests extends AndroidTestCase {
private static final boolean localLOGV = true;
public static final String TAG = "PackageHelperTests";
- protected final String PREFIX = "android.content.pm";
private static final String sInternalVolPath = "/data";
private static final String sAdoptedVolPath = "/mnt/expand/123";
@@ -88,11 +87,14 @@
UUID internalUuid = UUID.randomUUID();
UUID adoptedUuid = UUID.randomUUID();
UUID publicUuid = UUID.randomUUID();
- Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
+ Mockito.when(storageManager.getStorageBytesUntilLow(internalFile))
+ .thenReturn(sInternalSize);
Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
- Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid);
- Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile)))
+ .thenReturn(internalUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile)))
+ .thenReturn(adoptedUuid);
Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid);
Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt()))
.thenReturn(sInternalSize);
@@ -103,7 +105,7 @@
return storageManager;
}
- private static final class MockedInterface extends PackageHelper.TestableInterface {
+ private static final class MockedInterface extends InstallLocationUtils.TestableInterface {
private boolean mForceAllowOnExternal = false;
private boolean mAllow3rdPartyOnInternal = true;
private ApplicationInfo mApplicationInfo = null;
@@ -164,25 +166,25 @@
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
@@ -192,7 +194,7 @@
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -202,7 +204,7 @@
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -212,7 +214,7 @@
mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -222,7 +224,7 @@
mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch(IOException e) {
@@ -240,13 +242,13 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
}
@@ -260,25 +262,25 @@
appInfo.volumeUuid = sAdoptedVolUuid;
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
@@ -292,7 +294,7 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -302,7 +304,7 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -312,7 +314,7 @@
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -322,7 +324,7 @@
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
try {
- PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
fail("Expected exception was not thrown " + appInfo.volumeUuid);
} catch (IOException e) {
@@ -336,28 +338,28 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sAdoptedVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the volume with bigger available space.
assertEquals(sAdoptedVolUuid, volume);
@@ -371,20 +373,20 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
true /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
true /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sInternalVolUuid, volume);
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch (IOException e) {
@@ -395,7 +397,7 @@
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
@@ -407,7 +409,7 @@
mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
false /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the non-internal volume.
assertEquals(sAdoptedVolUuid, volume);
@@ -415,7 +417,7 @@
appInfo = null;
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
// Should return the non-internal volume.
assertEquals(sAdoptedVolUuid, volume);
@@ -428,7 +430,7 @@
true /*allow 3rd party on internal*/);
String volume = null;
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal ONLY*/,
1000000 /*size too big*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
@@ -445,7 +447,7 @@
false /*allow 3rd party on internal*/);
String volume = null;
try {
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
fail("Expected exception in resolveInstallVolume was not thrown");
} catch (IOException e) {
@@ -456,7 +458,7 @@
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
@@ -474,7 +476,7 @@
mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
false /*allow 3rd party on internal*/);
String volume = null;
- volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+ volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name",
1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
assertEquals(sAdoptedVolUuid, volume);
}
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index 104f077..f7ca822 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -146,6 +146,7 @@
@Test
public void testValidateWaveformBuilder() {
+ // Cover builder methods
VibrationEffect.startWaveform(targetAmplitude(1))
.addTransition(Duration.ofSeconds(1), targetAmplitude(0.5f), targetFrequency(100))
.addTransition(Duration.ZERO, targetAmplitude(0f), targetFrequency(200))
@@ -158,6 +159,39 @@
.build()
.validate();
+ // Make sure class summary javadoc examples compile and are valid.
+ // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE WaveformBuilder javadocs.
+ VibrationEffect.startWaveform(targetFrequency(60))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
+ .addSustain(Duration.ofMillis(200))
+ .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
+ .build()
+ .validate();
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addOffDuration(Duration.ofMillis(20))
+ .repeatEffectIndefinitely(
+ VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ .addSustain(Duration.ofMillis(10))
+ .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
+ .addSustain(Duration.ofMillis(30))
+ .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
+ .addSustain(Duration.ofMillis(50))
+ .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
+ .build())
+ .compose()
+ .validate();
+ VibrationEffect.createWaveform(new long[]{10, 20, 30}, new int[]{51, 102, 204}, -1)
+ .validate();
+ VibrationEffect.startWaveform(targetAmplitude(0.2f))
+ .addSustain(Duration.ofMillis(10))
+ .addTransition(Duration.ZERO, targetAmplitude(0.4f))
+ .addSustain(Duration.ofMillis(20))
+ .addTransition(Duration.ZERO, targetAmplitude(0.8f))
+ .addSustain(Duration.ofMillis(30))
+ .build()
+ .validate();
+
assertThrows(IllegalStateException.class,
() -> VibrationEffect.startWaveform().build().validate());
assertThrows(IllegalArgumentException.class, () -> targetAmplitude(-2));
@@ -171,6 +205,7 @@
@Test
public void testValidateComposed() {
+ // Cover builder methods
VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.addEffect(TEST_ONE_SHOT)
@@ -178,11 +213,28 @@
.addOffDuration(Duration.ofMillis(100))
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0))
+ .compose()
+ .validate();
+ VibrationEffect.startComposition()
+ .repeatEffectIndefinitely(TEST_ONE_SHOT)
.compose()
.validate();
+ // Make sure class summary javadoc examples compile and are valid.
+ // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE Composition javadocs.
VibrationEffect.startComposition()
- .repeatEffectIndefinitely(TEST_ONE_SHOT)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
+ .compose()
+ .validate();
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .addOffDuration(Duration.ofMillis(10))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
+ .addOffDuration(Duration.ofMillis(50))
+ .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0))
.compose()
.validate();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f61e624..9f4ff7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -471,9 +471,10 @@
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
DisplayController displayController, Context context,
@ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor) {
return new Transitions(organizer, pool, displayController, context, mainExecutor,
- animExecutor);
+ mainHandler, animExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e592101..a2c2f59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -879,6 +879,7 @@
if (mMainUnfoldController != null && mSideUnfoldController != null) {
mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ updateUnfoldBounds();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 79c8a87..27b6dc5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -25,6 +25,11 @@
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
@@ -57,11 +62,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
+import android.os.Handler;
import android.os.IBinder;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -118,6 +129,7 @@
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final DevicePolicyManager mDevicePolicyManager;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -132,9 +144,24 @@
private ScreenRotationAnimation mRotationAnimation;
+ private Drawable mEnterpriseThumbnailDrawable;
+
+ private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean isDrawable = intent.getBooleanExtra(
+ EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false);
+ if (!isDrawable) {
+ return;
+ }
+ updateEnterpriseThumbnailDrawable();
+ }
+ };
+
DefaultTransitionHandler(@NonNull DisplayController displayController,
@NonNull TransactionPool transactionPool, Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mDisplayController = displayController;
mTransactionPool = transactionPool;
mContext = context;
@@ -143,9 +170,23 @@
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
mCurrentUserId = UserHandle.myUserId();
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ updateEnterpriseThumbnailDrawable();
+ mContext.registerReceiver(
+ mEnterpriseResourceUpdatedReceiver,
+ new IntentFilter(ACTION_DEVICE_POLICY_RESOURCE_UPDATED),
+ /* broadcastPermission = */ null,
+ mainHandler);
+
AttributeCache.init(context);
}
+ private void updateEnterpriseThumbnailDrawable() {
+ mEnterpriseThumbnailDrawable = mDevicePolicyManager.getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> mContext.getDrawable(R.drawable.ic_corp_badge));
+ }
+
@VisibleForTesting
static boolean isRotationSeamless(@NonNull TransitionInfo info,
DisplayController displayController) {
@@ -632,7 +673,7 @@
final boolean isClose = Transitions.isClosingType(change.getMode());
if (isOpen) {
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
- attachCrossProfileThunmbnailAnimation(animations, finishCallback, change,
+ attachCrossProfileThumbnailAnimation(animations, finishCallback, change,
cornerRadius);
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
@@ -642,13 +683,14 @@
}
}
- private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+ private void attachCrossProfileThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
- final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
- ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
final Rect bounds = change.getEndAbsBounds();
+ // Show the right drawable depending on the user we're transitioning to.
+ final Drawable thumbnailDrawable = change.getTaskInfo().userId == mCurrentUserId
+ ? mContext.getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable;
final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
- thumbnailDrawableRes, bounds);
+ thumbnailDrawable, bounds);
if (thumbnail == null) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 33a98b2..86b73fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -33,6 +33,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -123,7 +124,8 @@
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull DisplayController displayController, @NonNull Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
@@ -132,7 +134,7 @@
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
- animExecutor));
+ mainHandler, animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index e391713..0f4a06f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -54,7 +54,9 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
@@ -84,8 +86,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@@ -106,6 +106,7 @@
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@Before
public void setUp() {
@@ -752,7 +753,7 @@
private Transitions createTestTransitions() {
return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
- mContext, mMainExecutor, mAnimExecutor);
+ mContext, mMainExecutor, mMainHandler, mAnimExecutor);
}
//
// private class TestDisplayController extends DisplayController {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1b4c0bc..15a398d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -562,6 +562,7 @@
* Indicates the volume set/adjust call is for Bluetooth absolute volume
* @hide
*/
+ @SystemApi
public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6;
/**
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index d1bb41e..86dc6e0 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -15,39 +15,24 @@
*/
package android.media;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothProfile;
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Contains information about Bluetooth profile connection state changed
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class BtProfileConnectionInfo implements Parcelable {
- /** @hide */
- @IntDef({
- BluetoothProfile.A2DP,
- BluetoothProfile.A2DP_SINK,
- BluetoothProfile.HEADSET, // Can only be set by BtHelper
- BluetoothProfile.HEARING_AID,
- BluetoothProfile.LE_AUDIO,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BtProfile {}
-
- private final @BtProfile int mProfile;
+ private final int mProfile;
private final boolean mSupprNoisy;
private final int mVolume;
private final boolean mIsLeOutput;
- private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume,
+ private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume,
boolean isLeOutput) {
mProfile = profile;
mSupprNoisy = suppressNoisyIntent;
@@ -59,7 +44,7 @@
* Constructor used by BtHelper when a profile is connected
* {@hide}
*/
- public BtProfileConnectionInfo(@BtProfile int profile) {
+ public BtProfileConnectionInfo(int profile) {
this(profile, false, -1, false);
}
@@ -142,7 +127,7 @@
/**
* @return The profile connection
*/
- public @BtProfile int getProfile() {
+ public int getProfile() {
return mProfile;
}
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 06dc498..0045018 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -64,8 +64,11 @@
@NonNull
private final List<String> mPreferredFeatures;
+ @NonNull
private final List<String> mRequiredFeatures;
+ @NonNull
private final List<String> mPackagesOrder;
+ @NonNull
private final List<String> mAllowedPackages;
private final boolean mShouldPerformActiveScan;
@@ -118,7 +121,6 @@
* Gets the required features of routes that media router would like to discover.
* <p>
* Routes that have all the required features will be discovered.
- * This precedes {@link #getPreferredFeatures()}.
* They may include predefined features such as
* {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
* or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
@@ -133,12 +135,16 @@
/**
* Gets the ordered list of package names used to remove duplicate routes.
* <p>
- * When the app enables duplicate removal, all the routes that have the same deduplication ID
- * except one from the provider whose package name appears first in the list will be removed.
+ * Duplicate route removal is enabled if the returned list is non-empty. Routes are deduplicated
+ * based on their {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}. If two routes
+ * have a deduplication ID in common, only the route from the provider whose package name is
+ * first in the provided list will remain.
+ *
+ * @see #shouldRemoveDuplicates()
*/
@NonNull
public List<String> getDeduplicationPackageOrder() {
- return mPackagesOrder == null ? List.of() : mPackagesOrder;
+ return mPackagesOrder;
}
/**
@@ -164,10 +170,9 @@
}
/**
- * Gets whether duplicate routes should be removed.
- * <p>
- * If it is {@code true}, only one of routes that have
- * the same deduplication ID will be obtained.
+ * Gets whether duplicate routes removal is enabled.
+ *
+ * @see #getDeduplicationPackageOrder()
*/
public boolean shouldRemoveDuplicates() {
return !mPackagesOrder.isEmpty();
@@ -338,23 +343,20 @@
}
/**
- * Sets the order of packages when removing duplicate routes.
+ * Sets the order of packages to use when removing duplicate routes.
* <p>
- * Routes are removed based on its
- * {@link MediaRoute2Info#getDeduplicationIds() deduplication ID}.
- * If two routes have the same ID, even if they are from different providers,
- * one of them is removed from the list.
- * <p>
- * Routes from the provider whose package name appears first in the given package order
- * will remain.
- * If unspecified, any route can be selected.
+ * Routes are deduplicated based on their
+ * {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}.
+ * If two routes have a deduplication ID in common, only the route from the provider whose
+ * package name is first in the provided list will remain.
*
- * @param packageOrder list of package names for choosing routes to be removed or
- * {@code null} not to remove duplicate routes.
+ * @param packageOrder ordered list of package names used to remove duplicate routes, or an
+ * empty list if deduplication should not be enabled.
*/
@NonNull
- public Builder setDeduplicationPackageOrder(@Nullable List<String> packageOrder) {
- mPackageOrder = (packageOrder == null) ? null : List.copyOf(packageOrder);
+ public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) {
+ Objects.requireNonNull(packageOrder, "packageOrder must not be null");
+ mPackageOrder = List.copyOf(packageOrder);
return this;
}
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
deleted file mode 100644
index 8aca1d6..0000000
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2011 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.StrictMode;
-import android.util.Log;
-
-import dalvik.system.SocketTagger;
-
-import java.io.FileDescriptor;
-import java.net.SocketException;
-
-/**
- * Assigns tags to sockets for traffic stats.
- * @hide
- */
-public final class NetworkManagementSocketTagger extends SocketTagger {
- private static final String TAG = "NetworkManagementSocketTagger";
- private static final boolean LOGD = false;
-
- private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
- @Override
- protected SocketTags initialValue() {
- return new SocketTags();
- }
- };
-
- public static void install() {
- SocketTagger.set(new NetworkManagementSocketTagger());
- }
-
- public static int setThreadSocketStatsTag(int tag) {
- final int old = threadSocketTags.get().statsTag;
- threadSocketTags.get().statsTag = tag;
- return old;
- }
-
- public static int getThreadSocketStatsTag() {
- return threadSocketTags.get().statsTag;
- }
-
- public static int setThreadSocketStatsUid(int uid) {
- final int old = threadSocketTags.get().statsUid;
- threadSocketTags.get().statsUid = uid;
- return old;
- }
-
- public static int getThreadSocketStatsUid() {
- return threadSocketTags.get().statsUid;
- }
-
- @Override
- public void tag(FileDescriptor fd) throws SocketException {
- final SocketTags options = threadSocketTags.get();
- if (LOGD) {
- Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
- + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
- }
- if (options.statsTag == -1) {
- StrictMode.noteUntaggedSocket();
- }
- // TODO: skip tagging when options would be no-op
- tagSocketFd(fd, options.statsTag, options.statsUid);
- }
-
- private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
- if (tag == -1 && uid == -1) return;
-
- final int errno = native_tagSocketFd(fd, tag, uid);
- if (errno < 0) {
- Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
- + tag + ", "
- + uid + ") failed with errno" + errno);
- }
- }
-
- @Override
- public void untag(FileDescriptor fd) throws SocketException {
- if (LOGD) {
- Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
- }
- unTagSocketFd(fd);
- }
-
- private void unTagSocketFd(FileDescriptor fd) {
- final SocketTags options = threadSocketTags.get();
- if (options.statsTag == -1 && options.statsUid == -1) return;
-
- final int errno = native_untagSocketFd(fd);
- if (errno < 0) {
- Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
- }
- }
-
- public static class SocketTags {
- public int statsTag = -1;
- public int statsUid = -1;
- }
-
- private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
- private static native int native_untagSocketFd(FileDescriptor fd);
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 1d0ae99..b65e976 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -37,7 +37,7 @@
import android.widget.Button;
import com.android.internal.app.AlertActivity;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -154,8 +154,8 @@
final PackageLite pkg = result.getResult();
params.setAppPackageName(pkg.getPackageName());
params.setInstallLocation(pkg.getInstallLocation());
- params.setSize(
- PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
+ params.setSize(InstallLocationUtils.calculateInstalledSize(pkg,
+ params.abiOverride));
}
} catch (IOException e) {
Log.e(LOG_TAG,
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 3f322d6..f7b2974 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -16,8 +16,11 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
@@ -102,8 +105,11 @@
if (mDisabledSummary) {
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
if (summaryView != null) {
- final CharSequence disabledText = summaryView.getContext().getText(
- R.string.disabled_by_admin_summary_text);
+ final CharSequence disabledText = mContext
+ .getSystemService(DevicePolicyManager.class)
+ .getString(CONTROLLED_BY_ADMIN_SUMMARY,
+ () -> summaryView.getContext().getString(
+ R.string.disabled_by_admin_summary_text));
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
} else if (mDisabledByAppOps) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d73e45e..883e080 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,7 +1,10 @@
package com.android.settingslib;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
+
import android.annotation.ColorInt;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -124,7 +127,8 @@
String name = info != null ? info.name : null;
if (info.isManagedProfile()) {
// We use predefined values for managed profiles
- return context.getString(R.string.managed_user_title);
+ return context.getSystemService(DevicePolicyManager.class).getString(
+ WORK_PROFILE_USER_LABEL, () -> context.getString(R.string.managed_user_title));
} else if (info.isGuest()) {
name = context.getString(R.string.user_guest);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index dc7632d..b851232 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -46,7 +46,7 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
import com.android.internal.util.XmlUtils;
@@ -783,7 +783,7 @@
+ " VALUES(?,?);");
loadSetting(stmt, Global.SET_INSTALL_LOCATION, 0);
loadSetting(stmt, Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -2534,7 +2534,7 @@
loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0);
loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
// Set default cdma emergency tone
loadSetting(stmt, Settings.Global.EMERGENCY_TONE, 0);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f35f5dd..2e6a58f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -301,6 +301,7 @@
<!-- For clipboard overlay -->
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+ <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 2a3761e..7e31909 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -17,6 +17,7 @@
<com.android.systemui.clipboardoverlay.DraggableConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:theme="@style/Screenshot"
android:alpha="0"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -50,7 +51,8 @@
<LinearLayout
android:id="@+id/actions"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:animateLayoutChanges="true">
<include layout="@layout/screenshot_action_chip"
android:id="@+id/remote_copy_chip"/>
<include layout="@layout/screenshot_action_chip"
@@ -64,7 +66,7 @@
android:layout_marginStart="@dimen/overlay_offset_x"
android:layout_marginBottom="@dimen/overlay_offset_y"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
android:elevation="@dimen/overlay_preview_elevation"
app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index db69924..de136de 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -85,7 +85,7 @@
Contract: Pixel with fillColor blended over backgroundColor blended over translucent should
equal to singleToneColor blended over translucent. -->
<declare-styleable name="TonedIcon">
- <attr name="backgroundColor" format="integer" />
+ <attr name="iconBackgroundColor" format="integer" />
<attr name="fillColor" format="integer" />
<attr name="singleToneColor" format="integer" />
<attr name="homeHandleColor" format="integer" />
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ac98739..57f1f3f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -404,19 +404,19 @@
</style>
<style name="DualToneLightTheme">
- <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
<item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
<item name="homeHandleColor">@color/navigation_bar_home_handle_light_color</item>
</style>
<style name="DualToneDarkTheme">
- <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
<item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item>
<item name="homeHandleColor">@color/navigation_bar_home_handle_dark_color</item>
</style>
<style name="QSHeaderDarkTheme">
- <item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
+ <item name="iconBackgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
<item name="fillColor">@color/dark_mode_qs_icon_color_dual_tone_fill</item>
<item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
new file mode 100644
index 0000000..ffab3cd
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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.systemui.shared.animation
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.lang.ref.WeakReference
+
+/**
+ * Translates items away/towards the hinge when the device is opened/closed, according to the
+ * direction specified in [ViewIdToTranslate.direction], for a maximum of [translationMax] when
+ * progresses are 0.
+ */
+class UnfoldConstantTranslateAnimator(
+ private val viewsIdToTranslate: Set<ViewIdToTranslate>,
+ private val progressProvider: UnfoldTransitionProgressProvider
+) : TransitionProgressListener {
+
+ private var viewsToTranslate = listOf<ViewToTranslate>()
+ private lateinit var rootView: ViewGroup
+ private var translationMax = 0f
+
+ fun init(rootView: ViewGroup, translationMax: Float) {
+ this.rootView = rootView
+ this.translationMax = translationMax
+ progressProvider.addCallback(this)
+ }
+
+ override fun onTransitionStarted() {
+ registerViewsForAnimation(rootView, viewsIdToTranslate)
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ translateViews(progress)
+ }
+
+ override fun onTransitionFinished() {
+ translateViews(progress = 1f)
+ }
+
+ private fun translateViews(progress: Float) {
+ // progress == 0 -> -translationMax
+ // progress == 1 -> 0
+ val xTrans = (progress - 1f) * translationMax
+ viewsToTranslate.forEach { (view, direction, shouldBeAnimated) ->
+ if (shouldBeAnimated()) {
+ view.get()?.translationX = xTrans * direction.multiplier
+ }
+ }
+ }
+
+ /** Finds in [parent] all views specified by [ids] and register them for the animation. */
+ private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) {
+ viewsToTranslate =
+ ids.mapNotNull { (id, dir, pred) ->
+ parent.findViewById<View>(id)?.let { view ->
+ ViewToTranslate(WeakReference(view), dir, pred)
+ }
+ }
+ }
+
+ /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */
+ data class ViewIdToTranslate(
+ val viewId: Int,
+ val direction: Direction,
+ val shouldBeAnimated: () -> Boolean = { true }
+ )
+
+ private data class ViewToTranslate(
+ val view: WeakReference<View>,
+ val direction: Direction,
+ val shouldBeAnimated: () -> Boolean
+ )
+
+ /** Direction of the animation. */
+ enum class Direction(val multiplier: Float) {
+ LEFT(-1f),
+ RIGHT(1f),
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index cb25e1a..89d6fb5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -17,11 +17,13 @@
package com.android.keyguard
import android.content.Context
-import android.view.View
import android.view.ViewGroup
import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
import com.android.systemui.unfold.SysUIUnfoldScope
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import javax.inject.Inject
@@ -30,84 +32,37 @@
* the set of ids, which also dictact which direction to move and when, via a filter function.
*/
@SysUIUnfoldScope
-class KeyguardUnfoldTransition @Inject constructor(
- val context: Context,
- val unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
+class KeyguardUnfoldTransition
+@Inject
+constructor(
+ private val context: Context,
+ unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
) {
- companion object {
- final val LEFT = -1
- final val RIGHT = 1
- }
+ /** Certain views only need to move if they are not currently centered */
+ var statusViewCentered = false
private val filterSplitShadeOnly = { !statusViewCentered }
private val filterNever = { true }
- private val ids = setOf(
- Triple(R.id.keyguard_status_area, LEFT, filterNever),
- Triple(R.id.controls_button, LEFT, filterNever),
- Triple(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
- Triple(R.id.lockscreen_clock_view, LEFT, filterNever),
- Triple(R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- Triple(R.id.wallet_button, RIGHT, filterNever)
- )
- private var parent: ViewGroup? = null
- private var views = listOf<Triple<View, Int, () -> Boolean>>()
- private var xTranslationMax = 0f
-
- /**
- * Certain views only need to move if they are not currently centered
- */
- var statusViewCentered = false
-
- init {
- unfoldProgressProvider.addCallback(
- object : TransitionProgressListener {
- override fun onTransitionStarted() {
- findViews()
- }
-
- override fun onTransitionProgress(progress: Float) {
- translateViews(progress)
- }
-
- override fun onTransitionFinished() {
- translateViews(1f)
- }
- }
- )
+ private val translateAnimator by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever),
+ ViewIdToTranslate(R.id.controls_button, LEFT, filterNever),
+ ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
+ ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
+ ViewIdToTranslate(
+ R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
+ ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
+ progressProvider = unfoldProgressProvider)
}
- /**
- * Relies on the [parent] to locate views to translate
- */
+ /** Relies on the [parent] to locate views to translate. */
fun setup(parent: ViewGroup) {
- this.parent = parent
- xTranslationMax = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_unfold_translation_x).toFloat()
- }
-
- /**
- * Manually translate views based on set direction. At the moment
- * [UnfoldMoveFromCenterAnimator] exists but moves all views a dynamic distance
- * from their mid-point. This code instead will only ever translate by a fixed amount.
- */
- private fun translateViews(progress: Float) {
- val xTrans = progress * xTranslationMax - xTranslationMax
- views.forEach {
- (view, direction, pred) -> if (pred()) {
- view.setTranslationX(xTrans * direction)
- }
- }
- }
-
- private fun findViews() {
- parent?.let { p ->
- views = ids.mapNotNull {
- (id, direction, pred) -> p.findViewById<View>(id)?.let {
- Triple(it, direction, pred)
- }
- }
- }
+ val translationMax =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
+ translateAnimator.init(parent, translationMax)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f2d0427..cc10b02 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1196,6 +1196,21 @@
return fingerprintAllowed || faceAllowed;
}
+ /**
+ * Returns whether the user is unlocked with a biometric that is currently bypassing
+ * the lock screen.
+ */
+ public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) {
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
+ // fingerprint always bypasses
+ boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
+ boolean faceAllowed = face != null && face.mAuthenticated
+ && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
+ return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass();
+ }
+
public boolean getUserTrustIsManaged(int userId) {
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
index fdc3229..2b8d3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
@@ -54,11 +54,11 @@
Utils.getThemeAttr(context, R.attr.lightIconTheme))
darkColor = Color(
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.singleToneColor),
- Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.backgroundColor),
+ Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.iconBackgroundColor),
Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.fillColor))
lightColor = Color(
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.singleToneColor),
- Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.backgroundColor),
+ Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.iconBackgroundColor),
Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor))
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 0e1cd51..72b40d4 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -63,7 +63,8 @@
mClipboardOverlayController =
new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
}
- mClipboardOverlayController.setClipData(mClipboardManager.getPrimaryClip());
+ mClipboardOverlayController.setClipData(
+ mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource());
mClipboardOverlayController.setOnSessionCompleteListener(() -> {
// Session is complete, free memory until it's needed again.
mClipboardOverlayController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index b6bcb87..12759f48 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -182,6 +182,7 @@
withWindowAttached(() -> {
mWindow.setContentView(mView);
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+ mView.requestLayout();
mView.post(this::animateIn);
});
@@ -213,7 +214,7 @@
mContext.sendBroadcast(new Intent(COPY_OVERLAY_ACTION), SELF_PERMISSION);
}
- void setClipData(ClipData clipData) {
+ void setClipData(ClipData clipData, String clipSource) {
reset();
if (clipData == null || clipData.getItemCount() == 0) {
showTextPreview(mContext.getResources().getString(
@@ -221,7 +222,7 @@
} else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
ClipData.Item item = clipData.getItemAt(0);
if (item.getTextLinks() != null) {
- AsyncTask.execute(() -> classifyText(clipData.getItemAt(0)));
+ AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
}
showEditableText(item.getText());
} else if (clipData.getItemAt(0).getUri() != null) {
@@ -238,7 +239,7 @@
mOnSessionCompleteListener = runnable;
}
- private void classifyText(ClipData.Item item) {
+ private void classifyText(ClipData.Item item, String source) {
ArrayList<RemoteAction> actions = new ArrayList<>();
for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
TextClassification classification = mTextClassifier.classifyText(
@@ -246,14 +247,14 @@
actions.addAll(classification.getActions());
}
mView.post(() -> {
- for (ScreenshotActionChip chip : mActionChips) {
- mActionContainer.removeView(chip);
- }
- mActionChips.clear();
+ resetActionChips();
for (RemoteAction action : actions) {
- ScreenshotActionChip chip = constructActionChip(action);
- mActionContainer.addView(chip);
- mActionChips.add(chip);
+ Intent targetIntent = action.getActionIntent().getIntent();
+ if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) {
+ ScreenshotActionChip chip = constructActionChip(action);
+ mActionContainer.addView(chip);
+ mActionChips.add(chip);
+ }
}
});
}
@@ -451,13 +452,17 @@
}
}
- private void reset() {
- mView.setTranslationX(0);
- mView.setAlpha(0);
+ private void resetActionChips() {
for (ScreenshotActionChip chip : mActionChips) {
mActionContainer.removeView(chip);
}
mActionChips.clear();
+ }
+
+ private void reset() {
+ mView.setTranslationX(0);
+ mView.setAlpha(0);
+ resetActionChips();
mTimeoutHandler.cancelTimeout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 90ed3c6..be45a62 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -104,7 +104,8 @@
private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
private static final int MAX_NUM_LOGGED_GESTURES = 10;
- static final boolean DEBUG_MISSING_GESTURE = false;
+ // Temporary log until b/202433017 is resolved
+ static final boolean DEBUG_MISSING_GESTURE = true;
static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
private ISystemGestureExclusionListener mGestureExclusionListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index abfdfaf..391525e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -27,6 +27,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
@@ -47,6 +48,7 @@
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.wm.shell.bubbles.Bubbles;
@@ -98,6 +100,8 @@
private final ForegroundServiceSectionController mFgsSectionController;
private final NotifPipelineFlags mNotifPipelineFlags;
private AssistantFeedbackController mAssistantFeedbackController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final Context mContext;
private NotificationPresenter mPresenter;
@@ -129,7 +133,9 @@
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mHandler = mainHandler;
mFeatureFlags = featureFlags;
@@ -149,6 +155,8 @@
mDynamicChildBindController = dynamicChildBindController;
mLowPriorityInflationHelper = lowPriorityInflationHelper;
mAssistantFeedbackController = assistantFeedbackController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStateController = keyguardStateController;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -174,6 +182,11 @@
beginUpdate();
+ boolean dynamicallyUnlocked = mDynamicPrivacyController.isDynamicallyUnlocked()
+ && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ && mKeyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
+ KeyguardUpdateMonitor.getCurrentUser()))
+ && !mKeyguardStateController.isKeyguardGoingAway();
List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
@@ -192,7 +205,7 @@
boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId);
boolean userPublic = devicePublic
|| mLockscreenUserManager.isLockscreenPublicMode(userId);
- if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked()
+ if (userPublic && dynamicallyUnlocked
&& (userId == currentUserId || userId == UserHandle.USER_ALL
|| !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) {
userPublic = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index d574cda..e3d0d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -23,6 +23,7 @@
import android.service.dreams.IDreamManager;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
@@ -72,6 +73,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tracing.ProtoTracer;
@@ -214,7 +216,9 @@
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController) {
return new NotificationViewHierarchyManager(
context,
mainHandler,
@@ -231,7 +235,9 @@
dynamicChildBindController,
lowPriorityInflationHelper,
assistantFeedbackController,
- notifPipelineFlags);
+ notifPipelineFlags,
+ keyguardUpdateMonitor,
+ keyguardStateController);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 0ce07cb..22300d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -98,6 +98,8 @@
setupInvalidateNotifListCallbacks();
// Filter at the "finalize" stage so that views remain bound by PreparationCoordinator
pipeline.addFinalizeFilter(mNotifFilter);
+
+ updateSectionHeadersVisibility();
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
@@ -164,6 +166,8 @@
}
}
+ // TODO(b/206118999): merge this class with SensitiveContentCoordinator which also depends on
+ // these same updates
private void setupInvalidateNotifListCallbacks() {
// register onKeyguardShowing callback
mKeyguardStateController.addCallback(mKeyguardCallback);
@@ -220,10 +224,7 @@
}
private void invalidateListFromFilter(String reason) {
- boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
- boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders();
- boolean showSections = !onKeyguard && !neverShowSections;
- mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections);
+ updateSectionHeadersVisibility();
mNotifFilter.invalidateList();
}
@@ -235,6 +236,13 @@
1) == 0;
}
+ private void updateSectionHeadersVisibility() {
+ boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders();
+ boolean showSections = !onKeyguard && !neverShowSections;
+ mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections);
+ }
+
private final KeyguardStateController.Callback mKeyguardCallback =
new KeyguardStateController.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index a115e04..9c82cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -17,7 +17,10 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -26,6 +29,7 @@
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Module
import dagger.Provides
@@ -36,9 +40,13 @@
@CoordinatorScope
fun provideCoordinator(
dynamicPrivacyController: DynamicPrivacyController,
- lockscreenUserManager: NotificationLockscreenUserManager
+ lockscreenUserManager: NotificationLockscreenUserManager,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ statusBarStateController: StatusBarStateController,
+ keyguardStateController: KeyguardStateController
): SensitiveContentCoordinator =
- SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager)
+ SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager,
+ keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
}
/** Coordinates re-inflation and post-processing of sensitive notification content. */
@@ -46,7 +54,10 @@
private class SensitiveContentCoordinatorImpl(
private val dynamicPrivacyController: DynamicPrivacyController,
- private val lockscreenUserManager: NotificationLockscreenUserManager
+ private val lockscreenUserManager: NotificationLockscreenUserManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val statusBarStateController: StatusBarStateController,
+ private val keyguardStateController: KeyguardStateController
) : Invalidator("SensitiveContentInvalidator"),
SensitiveContentCoordinator,
DynamicPrivacyController.Listener,
@@ -61,6 +72,19 @@
override fun onDynamicPrivacyChanged(): Unit = invalidateList()
override fun onBeforeRenderList(entries: List<ListEntry>) {
+ if (keyguardStateController.isKeyguardGoingAway() ||
+ statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ // don't update yet if:
+ // - the keyguard is currently going away
+ // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash)
+
+ // TODO(b/206118999): merge this class with KeyguardCoordinator which ensures the
+ // dependent state changes invalidate the pipeline
+ return
+ }
+
val currentUserId = lockscreenUserManager.currentUserId
val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId)
val deviceSensitive = devicePublic &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 28cd285..386e2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -104,19 +104,14 @@
views.remove(childNode.controller.view)
}
- if (childCompletelyRemoved && parentSpec == null) {
- // If both the child and the parent are being removed at the same time, then
- // keep the child attached to the parent for animation purposes
- logger.logSkippingDetach(childNode.label, parentNode.label)
- } else {
- logger.logDetachingChild(
- childNode.label,
- !childCompletelyRemoved,
- parentNode.label,
- newParentNode?.label)
- parentNode.removeChild(childNode, !childCompletelyRemoved)
- childNode.parent = null
- }
+ logger.logDetachingChild(
+ key = childNode.label,
+ isTransfer = !childCompletelyRemoved,
+ isParentRemoved = parentSpec == null,
+ oldParent = parentNode.label,
+ newParent = newParentNode?.label)
+ parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
+ childNode.parent = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index d274550..4c03572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -28,25 +28,18 @@
fun logDetachingChild(
key: String,
isTransfer: Boolean,
+ isParentRemoved: Boolean,
oldParent: String?,
newParent: String?
) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
bool1 = isTransfer
+ bool2 = isParentRemoved
str2 = oldParent
str3 = newParent
}, {
- "Detach $str1 isTransfer=$bool1 oldParent=$str2 newParent=$str3"
- })
- }
-
- fun logSkippingDetach(key: String, parent: String?) {
- buffer.log(TAG, LogLevel.DEBUG, {
- str1 = key
- str2 = parent
- }, {
- "Skipping detach of $str1 because its parent $str2 is also being detached"
+ "Detach $str1 isTransfer=$bool1 isParentRemoved=$bool2 oldParent=$str2 newParent=$str3"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index dbd22db..1f7d930 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -49,6 +49,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.Trace;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -1246,6 +1247,7 @@
}
private void reInflateViews() {
+ Trace.beginSection("ExpandableNotificationRow#reInflateViews");
// Let's update our childrencontainer. This is intentionally not guarded with
// mIsSummaryWithChildren since we might have had children but not anymore.
if (mChildrenContainer != null) {
@@ -1277,6 +1279,7 @@
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
+ Trace.endSection();
}
@Override
@@ -1737,6 +1740,29 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure"));
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
+
+ /** Generates and appends "(MessagingStyle)" type tag to passed string for tracing. */
+ @NonNull
+ private String appendTraceStyleTag(@NonNull String traceTag) {
+ if (!Trace.isEnabled()) {
+ return traceTag;
+ }
+
+ Class<? extends Notification.Style> style =
+ getEntry().getSbn().getNotification().getNotificationStyle();
+ if (style == null) {
+ return traceTag + "(nostyle)";
+ } else {
+ return traceTag + "(" + style.getSimpleName() + ")";
+ }
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mPublicLayout = findViewById(R.id.expandedPublic);
@@ -2542,6 +2568,7 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout"));
int intrinsicBefore = getIntrinsicHeight();
super.onLayout(changed, left, top, right, bottom);
if (intrinsicBefore != getIntrinsicHeight()
@@ -2555,6 +2582,7 @@
if (mLayoutListener != null) {
mLayoutListener.onLayout();
}
+ Trace.endSection();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 89b5aef..7c9df42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -344,9 +344,11 @@
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
final ExpandableView changingView = (ExpandableView) event.mChangingView;
boolean loggable = false;
+ boolean isHeadsUp = false;
String key = null;
if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
loggable = true;
+ isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp();
key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
}
if (event.animationType ==
@@ -358,6 +360,9 @@
// The position for this child was never generated, let's continue.
continue;
}
+ if (loggable && isHeadsUp) {
+ mLogger.logHUNViewAppearingWithAddEvent(key);
+ }
viewState.applyToView(changingView);
mNewAddChildren.add(changingView);
@@ -399,9 +404,18 @@
translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
}
+ Runnable postAnimation = changingView::removeFromTransientContainer;
+ if (loggable && isHeadsUp) {
+ mLogger.logHUNViewDisappearingWithRemoveEvent(key);
+ String finalKey = key;
+ postAnimation = () -> {
+ mLogger.disappearAnimationEnded(finalKey);
+ changingView.removeFromTransientContainer();
+ };
+ }
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, changingView::removeFromTransientContainer, null);
+ 0, postAnimation, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (mHostLayout.isFullySwipedOut(changingView)) {
@@ -431,8 +445,7 @@
// this only captures HEADS_UP_APPEAR animations, but HUNs can appear with normal
// ADD animations, which would not be logged here.
if (loggable) {
- mLogger.logHUNViewAppearing(
- ((ExpandableNotificationRow) changingView).getEntry().getKey());
+ mLogger.logHUNViewAppearing(key);
}
mTmpState.applyToView(changingView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index 4315265..77377af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -24,6 +24,22 @@
})
}
+ fun logHUNViewDisappearingWithRemoveEvent(key: String) {
+ buffer.log(TAG, LogLevel.ERROR, {
+ str1 = key
+ }, {
+ "Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE"
+ })
+ }
+
+ fun logHUNViewAppearingWithAddEvent(key: String) {
+ buffer.log(TAG, LogLevel.ERROR, {
+ str1 = key
+ }, {
+ "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD"
+ })
+ }
+
fun disappearAnimationEnded(key: String) {
buffer.log(TAG, LogLevel.INFO, {
str1 = key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt
new file mode 100644
index 0000000..ff48755
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.systemui.statusbar.phone
+
+import android.content.Context
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.SysUIUnfoldScope
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import javax.inject.Inject
+
+@SysUIUnfoldScope
+class NotificationPanelUnfoldAnimationController
+@Inject
+constructor(private val context: Context, progressProvider: NaturalRotationUnfoldProgressProvider) {
+
+ private val translateAnimator by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(R.id.quick_settings_panel, LEFT),
+ ViewIdToTranslate(R.id.notification_stack_scroller, RIGHT),
+ ViewIdToTranslate(R.id.rightLayout, RIGHT),
+ ViewIdToTranslate(R.id.clock, LEFT),
+ ViewIdToTranslate(R.id.date, LEFT)),
+ progressProvider = progressProvider)
+ }
+
+ fun setup(root: ViewGroup) {
+ val translationMax =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat()
+ translateAnimator.init(root, translationMax)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index bb0ed95..1870588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -669,6 +669,8 @@
private boolean mStatusViewCentered = true;
private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
+ private Optional<NotificationPanelUnfoldAnimationController>
+ mNotificationPanelUnfoldAnimationController;
private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
@@ -929,6 +931,8 @@
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
+ mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
+ SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
mCommunalSourceMonitorCallback = (source) -> {
mUiExecutor.execute(() -> setCommunalSource(source));
@@ -1064,6 +1068,8 @@
mTapAgainViewController.init();
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
+ mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
+ controller.setup(mNotificationContainerParent));
}
@Override
@@ -1319,6 +1325,7 @@
setKeyguardBottomAreaVisibility(mBarState, false);
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
+ mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index 091831f..ea61a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -113,14 +113,16 @@
* the animation ends
*/
fun shouldDelayKeyguardShow(): Boolean =
- animations.any { it.shouldPlayAnimation() }
+ animations.any { it.shouldDelayKeyguardShow() }
/**
* Return true while we want to ignore requests to show keyguard, we need to handle pending
* keyguard lock requests manually
+ *
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator.maybeHandlePendingLock]
*/
fun isKeyguardShowDelayed(): Boolean =
- animations.any { it.isAnimationPlaying() }
+ animations.any { it.isKeyguardShowDelayed() }
/**
* Return true to ignore requests to hide keyguard
@@ -211,6 +213,8 @@
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun shouldDelayKeyguardShow(): Boolean = false
+ fun isKeyguardShowDelayed(): Boolean = false
fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cc65ca02..0abe8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -136,6 +136,12 @@
globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
}
+ override fun shouldDelayKeyguardShow(): Boolean =
+ shouldPlayAnimation()
+
+ override fun isKeyguardShowDelayed(): Boolean =
+ isAnimationPlaying()
+
/**
* Animates in the provided keyguard view, ending in the same position that it will be in on
* AOD.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 9f20bc5..49e712d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -455,6 +455,13 @@
}
}
+ /**
+ * Returns whether the current user is a system user.
+ */
+ public boolean isSystemUser() {
+ return mUserTracker.getUserId() == UserHandle.USER_SYSTEM;
+ }
+
public void removeUserId(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
Log.w(TAG, "User " + userId + " could not removed.");
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index c481fc9..2e627a8 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -20,7 +20,6 @@
import android.os.PowerManager
import android.provider.Settings
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
@@ -28,7 +27,6 @@
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
-import dagger.Lazy
import javax.inject.Inject
/**
@@ -40,7 +38,6 @@
@Inject
constructor(
@Main private val handler: Handler,
- private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
@@ -57,7 +54,6 @@
statusBar.notificationPanelViewController.startFoldToAodAnimation {
// End action
isAnimationPlaying = false
- keyguardViewMediatorLazy.get().maybeHandlePendingLock()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 07f9c54..7350b37 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -18,6 +18,7 @@
import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -85,6 +86,8 @@
fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+ fun getNotificationPanelUnfoldAnimationController(): NotificationPanelUnfoldAnimationController
+
fun getFoldAodAnimationController(): FoldAodAnimationController
fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
new file mode 100644
index 0000000..7687432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service;
+
+/**
+ * The {@link Observer} interface specifies an entity which listeners
+ * can be informed of changes to the source, which will require updating. Note that this deals
+ * with changes to the source itself, not content which will be updated through the interface.
+ */
+public interface Observer {
+ /**
+ * Callback for receiving updates from the {@link Observer}.
+ */
+ interface Callback {
+ /**
+ * Invoked when the source has changed.
+ */
+ void onSourceChanged();
+ }
+
+ /**
+ * Adds a callback to receive future updates from the {@link Observer}.
+ */
+ void addCallback(Callback callback);
+
+ /**
+ * Removes a callback from receiving further updates.
+ * @param callback
+ */
+ void removeCallback(Callback callback);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
new file mode 100644
index 0000000..2ee7b20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PatternMatcher;
+import android.util.Log;
+
+import com.android.systemui.communal.CommunalSource;
+
+import com.google.android.collect.Lists;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.inject.Inject;
+
+/**
+ * {@link PackageObserver} allows for monitoring the system for changes relating to a particular
+ * package. This can be used by {@link CommunalSource} clients to detect when a related package
+ * has changed and reloading is necessary.
+ */
+public class PackageObserver implements Observer {
+ private static final String TAG = "PackageObserver";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "package added receiver - onReceive");
+ }
+
+ final Iterator<WeakReference<Callback>> iter = mCallbacks.iterator();
+ while (iter.hasNext()) {
+ final Callback callback = iter.next().get();
+ if (callback != null) {
+ callback.onSourceChanged();
+ } else {
+ iter.remove();
+ }
+ }
+ }
+ };
+
+ private final String mPackageName;
+ private final Context mContext;
+
+ @Inject
+ public PackageObserver(Context context, ComponentName component) {
+ mContext = context;
+ mPackageName = component.getPackageName();
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "addCallback:" + callback);
+ }
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // Only register for listening to package additions on first callback.
+ if (mCallbacks.size() > 1) {
+ return;
+ }
+
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mPackageName, PatternMatcher.PATTERN_LITERAL);
+ // Note that we directly register the receiver here as data schemes are not supported by
+ // BroadcastDispatcher.
+ mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "removeCallback:" + callback);
+ }
+ final boolean removed = mCallbacks.removeIf(el -> el.get() == callback);
+
+ if (removed && mCallbacks.isEmpty()) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
new file mode 100644
index 0000000..292c062
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service;
+
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.BASE_RECONNECT_DELAY_MS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.MAX_RECONNECT_ATTEMPTS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.MIN_CONNECTION_DURATION_MS;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.OBSERVER;
+import static com.android.systemui.util.service.dagger.ObservableServiceModule.SERVICE_CONNECTION;
+
+import android.util.Log;
+
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * The {@link PersistentConnectionManager} is responsible for maintaining a connection to a
+ * {@link ObservableServiceConnection}.
+ * @param <T> The transformed connection type handled by the service.
+ */
+public class PersistentConnectionManager<T> {
+ private static final String TAG = "PersistentConnManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final SystemClock mSystemClock;
+ private final DelayableExecutor mMainExecutor;
+ private final int mBaseReconnectDelayMs;
+ private final int mMaxReconnectAttempts;
+ private final int mMinConnectionDuration;
+ private final Observer mObserver;
+
+ private int mReconnectAttempts = 0;
+ private Runnable mCurrentReconnectCancelable;
+
+ private final ObservableServiceConnection<T> mConnection;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCurrentReconnectCancelable = null;
+ mConnection.bind();
+ }
+ };
+
+ private final Observer.Callback mObserverCallback = () -> initiateConnectionAttempt();
+
+ private final ObservableServiceConnection.Callback mConnectionCallback =
+ new ObservableServiceConnection.Callback() {
+ private long mStartTime;
+
+ @Override
+ public void onConnected(ObservableServiceConnection connection, Object proxy) {
+ mStartTime = mSystemClock.currentTimeMillis();
+ }
+
+ @Override
+ public void onDisconnected(ObservableServiceConnection connection, int reason) {
+ if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
+ initiateConnectionAttempt();
+ } else {
+ scheduleConnectionAttempt();
+ }
+ }
+ };
+
+ @Inject
+ public PersistentConnectionManager(
+ SystemClock clock,
+ DelayableExecutor mainExecutor,
+ @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
+ @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts,
+ @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs,
+ @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs,
+ @Named(OBSERVER) Observer observer) {
+ mSystemClock = clock;
+ mMainExecutor = mainExecutor;
+ mConnection = serviceConnection;
+ mObserver = observer;
+
+ mMaxReconnectAttempts = maxReconnectAttempts;
+ mBaseReconnectDelayMs = baseReconnectDelayMs;
+ mMinConnectionDuration = minConnectionDurationMs;
+ }
+
+ /**
+ * Begins the {@link PersistentConnectionManager} by connecting to the associated service.
+ */
+ public void start() {
+ mConnection.addCallback(mConnectionCallback);
+ mObserver.addCallback(mObserverCallback);
+ initiateConnectionAttempt();
+ }
+
+ /**
+ * Brings down the {@link PersistentConnectionManager}, disconnecting from the service.
+ */
+ public void stop() {
+ mConnection.removeCallback(mConnectionCallback);
+ mObserver.removeCallback(mObserverCallback);
+ mConnection.unbind();
+ }
+
+ private void initiateConnectionAttempt() {
+ // Reset attempts
+ mReconnectAttempts = 0;
+
+ // The first attempt is always a direct invocation rather than delayed.
+ mConnection.bind();
+ }
+
+ private void scheduleConnectionAttempt() {
+ // always clear cancelable if present.
+ if (mCurrentReconnectCancelable != null) {
+ mCurrentReconnectCancelable.run();
+ mCurrentReconnectCancelable = null;
+ }
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ if (DEBUG) {
+ Log.d(TAG, "exceeded max connection attempts.");
+ }
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
+ }
+
+ mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+ reconnectDelayMs);
+
+ mReconnectAttempts++;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
new file mode 100644
index 0000000..c62c957
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service.dagger;
+
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module containing components and parameters for
+ * {@link com.android.systemui.util.service.ObservableServiceConnection}
+ * and {@link com.android.systemui.util.service.PersistentConnectionManager}.
+ */
+@Module(subcomponents = {
+ PackageObserverComponent.class,
+})
+public class ObservableServiceModule {
+ public static final String MAX_RECONNECT_ATTEMPTS = "max_reconnect_attempts";
+ public static final String BASE_RECONNECT_DELAY_MS = "base_reconnect_attempts";
+ public static final String MIN_CONNECTION_DURATION_MS = "min_connection_duration_ms";
+ public static final String SERVICE_CONNECTION = "service_connection";
+ public static final String OBSERVER = "observer";
+
+ @Provides
+ @Named(MAX_RECONNECT_ATTEMPTS)
+ static int providesMaxReconnectAttempts(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_communalSourceMaxReconnectAttempts);
+ }
+
+ @Provides
+ @Named(BASE_RECONNECT_DELAY_MS)
+ static int provideBaseReconnectDelayMs(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_communalSourceReconnectBaseDelay);
+ }
+
+ @Provides
+ @Named(MIN_CONNECTION_DURATION_MS)
+ static int providesMinConnectionDuration(@Main Resources resources) {
+ return resources.getInteger(
+ R.integer.config_connectionMinDuration);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java
new file mode 100644
index 0000000..8ee39b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service.dagger;
+
+import android.content.ComponentName;
+
+import com.android.systemui.util.service.PackageObserver;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Generates a scoped {@link PackageObserver}.
+ */
+@Subcomponent
+public interface PackageObserverComponent {
+ /**
+ * Generates a {@link PackageObserverComponent} instance.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ PackageObserverComponent create(@BindsInstance ComponentName component);
+ }
+
+ /**
+ * Creates a {@link PackageObserver}.
+ */
+ PackageObserver getPackageObserver();
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 164f83d..6c1f008 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -20,23 +20,21 @@
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUnfoldTransition.Companion.LEFT
-import com.android.keyguard.KeyguardUnfoldTransition.Companion.RIGHT
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.util.mockito.capture
-import org.junit.Assert.assertEquals
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
/**
* Translates items away/towards the hinge when the device is opened/closed. This is controlled by
@@ -46,14 +44,11 @@
@RunWith(AndroidTestingRunner::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
- @Mock
- private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+ @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
- @Captor
- private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+ @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
- @Mock
- private lateinit var parent: ViewGroup
+ @Mock private lateinit var parent: ViewGroup
private lateinit var keyguardUnfoldTransition: KeyguardUnfoldTransition
private lateinit var progressListener: TransitionProgressListener
@@ -63,87 +58,35 @@
fun setup() {
MockitoAnnotations.initMocks(this)
- xTranslationMax = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_unfold_translation_x).toFloat()
+ xTranslationMax =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- keyguardUnfoldTransition = KeyguardUnfoldTransition(
- getContext(),
- progressProvider
- )
-
- verify(progressProvider).addCallback(capture(progressListenerCaptor))
- progressListener = progressListenerCaptor.value
+ keyguardUnfoldTransition = KeyguardUnfoldTransition(context, progressProvider)
keyguardUnfoldTransition.setup(parent)
keyguardUnfoldTransition.statusViewCentered = false
- }
- @Test
- fun onTransition_noMatchingIds() {
- // GIVEN no views matching any ids
- // WHEN the transition starts
- progressListener.onTransitionStarted()
- progressListener.onTransitionProgress(.1f)
-
- // THEN nothing... no exceptions
- }
-
- @Test
- fun onTransition_oneMovesLeft() {
- // GIVEN one view with a matching id
- val view = View(getContext())
- `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(view)
-
- moveAndValidate(listOf(view to LEFT))
- }
-
- @Test
- fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() {
- // GIVEN two views with a matching id
- val leftView = View(getContext())
- val rightView = View(getContext())
- `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(leftView)
- `when`(parent.findViewById<View>(R.id.notification_stack_scroller)).thenReturn(rightView)
-
- moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
- moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ verify(progressProvider).addCallback(capture(progressListenerCaptor))
+ progressListener = progressListenerCaptor.value
}
@Test
fun onTransition_centeredViewDoesNotMove() {
keyguardUnfoldTransition.statusViewCentered = true
- val view = View(getContext())
+ val view = View(context)
`when`(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
- moveAndValidate(listOf(view to 0))
- }
-
- private fun moveAndValidate(list: List<Pair<View, Int>>) {
- // Compare values as ints because -0f != 0f
-
- // WHEN the transition starts
progressListener.onTransitionStarted()
+ assertThat(view.translationX).isZero()
+
progressListener.onTransitionProgress(0f)
+ assertThat(view.translationX).isZero()
- list.forEach { (view, direction) ->
- assertEquals((-xTranslationMax * direction).toInt(), view.getTranslationX().toInt())
- }
+ progressListener.onTransitionProgress(0.5f)
+ assertThat(view.translationX).isZero()
- // WHEN the transition progresses, translation is updated
- progressListener.onTransitionProgress(.5f)
- list.forEach { (view, direction) ->
- assertEquals(
- (-xTranslationMax / 2f * direction).toInt(),
- view.getTranslationX().toInt()
- )
- }
-
- // WHEN the transition ends, translation is completed
- progressListener.onTransitionProgress(1f)
progressListener.onTransitionFinished()
- list.forEach { (view, _) ->
- assertEquals(0, view.getTranslationX().toInt())
- }
+ assertThat(view.translationX).isZero()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
new file mode 100644
index 0000000..3231415
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -0,0 +1,117 @@
+package com.android.systemui.shared.animation
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
+import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
+
+ @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider
+
+ @Mock private lateinit var parent: ViewGroup
+
+ @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+
+ private lateinit var animator: UnfoldConstantTranslateAnimator
+ private lateinit var progressListener: TransitionProgressListener
+
+ private val viewsIdToRegister =
+ setOf(
+ ViewIdToTranslate(LEFT_VIEW_ID, Direction.LEFT),
+ ViewIdToTranslate(RIGHT_VIEW_ID, Direction.RIGHT))
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ animator =
+ UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider)
+
+ animator.init(parent, MAX_TRANSLATION)
+
+ verify(progressProvider).addCallback(progressListenerCaptor.capture())
+ progressListener = progressListenerCaptor.value
+ }
+
+ @Test
+ fun onTransition_noMatchingIds() {
+ // GIVEN no views matching any ids
+ // WHEN the transition starts
+ progressListener.onTransitionStarted()
+ progressListener.onTransitionProgress(.1f)
+
+ // THEN nothing... no exceptions
+ }
+
+ @Test
+ fun onTransition_oneMovesLeft() {
+ // GIVEN one view with a matching id
+ val view = View(context)
+ whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(view)
+
+ moveAndValidate(listOf(view to LEFT))
+ }
+
+ @Test
+ fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() {
+ // GIVEN two views with a matching id
+ val leftView = View(context)
+ val rightView = View(context)
+ whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(leftView)
+ whenever(parent.findViewById<View>(RIGHT_VIEW_ID)).thenReturn(rightView)
+
+ moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT))
+ }
+
+ private fun moveAndValidate(list: List<Pair<View, Int>>) {
+ // Compare values as ints because -0f != 0f
+
+ // WHEN the transition starts
+ progressListener.onTransitionStarted()
+ progressListener.onTransitionProgress(0f)
+
+ list.forEach { (view, direction) ->
+ assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt())
+ }
+
+ // WHEN the transition progresses, translation is updated
+ progressListener.onTransitionProgress(.5f)
+ list.forEach { (view, direction) ->
+ assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt())
+ }
+
+ // WHEN the transition ends, translation is completed
+ progressListener.onTransitionProgress(1f)
+ progressListener.onTransitionFinished()
+ list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) }
+ }
+
+ companion object {
+ private val LEFT = Direction.LEFT.multiplier.toInt()
+ private val RIGHT = Direction.RIGHT.multiplier.toInt()
+
+ private const val MAX_TRANSLATION = 42f
+
+ private const val LEFT_VIEW_ID = 1
+ private const val RIGHT_VIEW_ID = 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 83f1d87..7fafb24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -35,6 +35,7 @@
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -56,6 +57,7 @@
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.bubbles.Bubbles;
import com.google.android.collect.Lists;
@@ -123,7 +125,9 @@
mock(DynamicChildBindController.class),
mock(LowPriorityInflationHelper.class),
mock(AssistantFeedbackController.class),
- mNotifPipelineFlags);
+ mNotifPipelineFlags,
+ mock(KeyguardUpdateMonitor.class),
+ mock(KeyguardStateController.class));
mViewHierarchyManager.setUpWithPresenter(mPresenter, mStackController, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 5fd4174..3f84c16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -19,8 +19,11 @@
import android.os.UserHandle
import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -28,9 +31,12 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import org.junit.Test
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -40,9 +46,13 @@
val dynamicPrivacyController: DynamicPrivacyController = mock()
val lockscreenUserManager: NotificationLockscreenUserManager = mock()
val pipeline: NotifPipeline = mock()
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+ val statusBarStateController: StatusBarStateController = mock()
+ val keyguardStateController: KeyguardStateController = mock()
val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule
- .provideCoordinator(dynamicPrivacyController, lockscreenUserManager)
+ .provideCoordinator(dynamicPrivacyController, lockscreenUserManager,
+ keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
@Test
fun onDynamicPrivacyChanged_invokeInvalidationListener() {
@@ -190,6 +200,28 @@
verify(entry.representativeEntry!!).setSensitive(true, true)
}
+ @Test
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
+ whenever(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD)
+ whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any()))
+ .thenReturn(true)
+
+ val entry = fakeNotification(2, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!, never()).setSensitive(any(), any())
+ }
+
private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
val mockUserHandle = mock<UserHandle>().apply {
whenever(identifier).thenReturn(notifUserId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
index 15ff555..ab71264 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import android.content.Context;
import android.testing.AndroidTestingRunner;
@@ -138,7 +139,7 @@
}
@Test
- public void testRemovedGroupsAreKeptTogether() {
+ public void testRemovedGroupsAreBrokenApart() {
// GIVEN a preexisting tree with a group
applySpecAndCheck(
node(mController1),
@@ -154,10 +155,10 @@
node(mController1)
);
- // THEN the group children are still attached to their parent
- assertEquals(mController2.getView(), mController3.getView().getParent());
- assertEquals(mController2.getView(), mController4.getView().getParent());
- assertEquals(mController2.getView(), mController5.getView().getParent());
+ // THEN the group children are no longer attached to their parent
+ assertNull(mController3.getView().getParent());
+ assertNull(mController4.getView().getParent());
+ assertNull(mController5.getView().getParent());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 5d7b154..d002cebe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -250,15 +250,17 @@
@Notification.GroupAlertBehavior int priorityGroupAlert,
@Notification.GroupAlertBehavior int siblingGroupAlert,
boolean expectAlertOverride) {
+ long when = 10000;
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(siblingGroupAlert, i, "sibling");
+ siblings[i] = mGroupTestHelper
+ .createChildNotification(siblingGroupAlert, i, "sibling", ++when);
}
NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority");
+ mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority", ++when);
NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary");
+ mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary", ++when);
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -322,17 +324,19 @@
@Test
public void testAlertOverrideWhenUpdatingSummaryAtEnd() {
+ long when = 10000;
int numSiblings = 2;
int groupAlert = Notification.GROUP_ALERT_SUMMARY;
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(groupAlert, i, "sibling");
+ siblings[i] =
+ mGroupTestHelper.createChildNotification(groupAlert, i, "sibling", ++when);
}
NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(groupAlert, 0, "priority");
+ mGroupTestHelper.createChildNotification(groupAlert, 0, "priority", ++when);
NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary");
+ mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary", ++when);
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index fa2a906..9a7e702 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -400,4 +400,16 @@
assertEquals(fgUserName, userSwitcherController.currentUserName)
}
+
+ @Test
+ fun isSystemUser_currentUserIsSystemUser_shouldReturnTrue() {
+ `when`(userTracker.userId).thenReturn(UserHandle.USER_SYSTEM)
+ assertEquals(true, userSwitcherController.isSystemUser)
+ }
+
+ @Test
+ fun isSystemUser_currentUserIsNotSystemUser_shouldReturnFalse() {
+ `when`(userTracker.userId).thenReturn(1)
+ assertEquals(false, userSwitcherController.isSystemUser)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
new file mode 100644
index 0000000..a2fd288
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 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.systemui.util.service;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class PackageObserverTest extends SysuiTestCase {
+ @Mock
+ Context mContext;
+
+ @Mock
+ Observer.Callback mCallback;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testChange() {
+ final PackageObserver observer = new PackageObserver(mContext,
+ ComponentName.unflattenFromString("com.foo.bar/baz"));
+ final ArgumentCaptor<BroadcastReceiver> receiverCapture =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ observer.addCallback(mCallback);
+
+ // Verify broadcast receiver registered.
+ verify(mContext).registerReceiver(receiverCapture.capture(), any(), anyInt());
+
+ // Simulate package change.
+ receiverCapture.getValue().onReceive(mContext, new Intent());
+
+ // Check that callback was informed.
+ verify(mCallback).onSourceChanged();
+
+ observer.removeCallback(mCallback);
+
+ // Make sure receiver is unregistered on last callback removal
+ verify(mContext).unregisterReceiver(receiverCapture.getValue());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
new file mode 100644
index 0000000..53d4a96
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 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.systemui.util.service;
+
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class PersistentConnectionManagerTest extends SysuiTestCase {
+ private static final int MAX_RETRIES = 5;
+ private static final int RETRY_DELAY_MS = 1000;
+ private static final int CONNECTION_MIN_DURATION_MS = 5000;
+
+ private FakeSystemClock mFakeClock = new FakeSystemClock();
+ private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
+
+ @Mock
+ private ObservableServiceConnection<Proxy> mConnection;
+
+ @Mock
+ private Observer mObserver;
+
+ private static class Proxy {
+ }
+
+ private PersistentConnectionManager<Proxy> mConnectionManager;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mConnectionManager = new PersistentConnectionManager<>(
+ mFakeClock,
+ mFakeExecutor,
+ mConnection,
+ MAX_RETRIES,
+ RETRY_DELAY_MS,
+ CONNECTION_MIN_DURATION_MS,
+ mObserver);
+ }
+
+ private ObservableServiceConnection.Callback<Proxy> captureCallbackAndSend(
+ ObservableServiceConnection<Proxy> mConnection, Proxy proxy) {
+ ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor =
+ ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class);
+
+ verify(mConnection).addCallback(connectionCallbackCaptor.capture());
+ verify(mConnection).bind();
+ Mockito.clearInvocations(mConnection);
+
+ final ObservableServiceConnection.Callback callback = connectionCallbackCaptor.getValue();
+ if (proxy != null) {
+ callback.onConnected(mConnection, proxy);
+ } else {
+ callback.onDisconnected(mConnection, 0);
+ }
+
+ return callback;
+ }
+
+ /**
+ * Validates initial connection.
+ */
+ @Test
+ public void testConnect() {
+ mConnectionManager.start();
+ captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class));
+ }
+
+ /**
+ * Ensures reconnection on disconnect.
+ */
+ @Test
+ public void testRetryOnBindFailure() {
+ mConnectionManager.start();
+ ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor =
+ ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class);
+
+ verify(mConnection).addCallback(connectionCallbackCaptor.capture());
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mConnection).bind();
+ Mockito.clearInvocations(mConnection);
+ connectionCallbackCaptor.getValue().onDisconnected(mConnection, 0);
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+ }
+
+ /**
+ * Ensures rebind on package change.
+ */
+ @Test
+ public void testAttemptOnPackageChange() {
+ mConnectionManager.start();
+ verify(mConnection).bind();
+ ArgumentCaptor<Observer.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(Observer.Callback.class);
+ captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class));
+
+ verify(mObserver).addCallback(callbackCaptor.capture());
+
+ callbackCaptor.getValue().onSourceChanged();
+ verify(mConnection).bind();
+ }
+}
diff --git a/services/Android.bp b/services/Android.bp
index 7e00986..af70692 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -95,6 +95,7 @@
":services.selectiontoolbar-sources",
":services.smartspace-sources",
":services.speech-sources",
+ ":services.startop.iorap-sources",
":services.systemcaptions-sources",
":services.translation-sources",
":services.texttospeech-sources",
@@ -150,6 +151,7 @@
"services.selectiontoolbar",
"services.smartspace",
"services.speech",
+ "services.startop",
"services.systemcaptions",
"services.translation",
"services.texttospeech",
@@ -205,6 +207,7 @@
" --hide-annotation android.annotation.Hide" +
" --hide InternalClasses" + // com.android.* classes are okay in this interface
// TODO: remove the --hide options below
+ " --hide-package com.google.android.startop.iorap" +
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//frameworks/base:__subpackages__"],
diff --git a/services/api/current.txt b/services/api/current.txt
index 50f0052..dcf7e64 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -39,6 +39,7 @@
public interface ActivityManagerLocal {
method public boolean canStartForegroundService(int, int, @NonNull String);
+ method public boolean startAndBindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int) throws android.os.TransactionTooLargeException;
}
}
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 0eb6b8d..627b0be 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -21,8 +21,6 @@
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothAdapter.nameForState;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
@@ -156,7 +154,7 @@
private void checkBleState() {
enforceInitialized();
- final boolean bleAvailable = isBleAvailable();
+ final boolean bleAvailable = mBtAdapter.isLeEnabled();
if (DEBUG) {
Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
}
@@ -183,16 +181,6 @@
}
}
- /**
- * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
- * access level, so it's not accessible from here.
- */
- private boolean isBleAvailable() {
- final int state = mBtAdapter.getLeState();
- if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
- return state == STATE_ON || state == STATE_BLE_ON;
- }
-
@MainThread
private void startScan() {
enforceInitialized();
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index e5a7b4e..8a6b54f 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,7 +19,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -32,15 +31,21 @@
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.ExtconUEventObserver.ExtconInfo;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* DockObserver monitors for a docking station.
@@ -48,9 +53,6 @@
final class DockObserver extends SystemService {
private static final String TAG = "DockObserver";
- private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
- private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
-
private static final int MSG_DOCK_STATE_CHANGED = 0;
private final PowerManager mPowerManager;
@@ -69,6 +71,92 @@
private final boolean mAllowTheaterModeWakeFromDock;
+ private final List<ExtconStateConfig> mExtconStateConfigs;
+
+ static final class ExtconStateProvider {
+ private final Map<String, String> mState;
+
+ ExtconStateProvider(Map<String, String> state) {
+ mState = state;
+ }
+
+ String getValue(String key) {
+ return mState.get(key);
+ }
+
+
+ static ExtconStateProvider fromString(String stateString) {
+ Map<String, String> states = new HashMap<>();
+ String[] lines = stateString.split("\n");
+ for (String line : lines) {
+ String[] fields = line.split("=");
+ if (fields.length == 2) {
+ states.put(fields[0], fields[1]);
+ } else {
+ Slog.e(TAG, "Invalid line: " + line);
+ }
+ }
+ return new ExtconStateProvider(states);
+ }
+
+ static ExtconStateProvider fromFile(String stateFilePath) {
+ char[] buffer = new char[1024];
+ try (FileReader file = new FileReader(stateFilePath)) {
+ int len = file.read(buffer, 0, 1024);
+ String stateString = (new String(buffer, 0, len)).trim();
+ return ExtconStateProvider.fromString(stateString);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "No state file found at: " + stateFilePath);
+ return new ExtconStateProvider(new HashMap<>());
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ return new ExtconStateProvider(new HashMap<>());
+ }
+ }
+ }
+
+ /**
+ * Represents a mapping from extcon state to EXTRA_DOCK_STATE value. Each
+ * instance corresponds to an entry in config_dockExtconStateMapping.
+ */
+ private static final class ExtconStateConfig {
+
+ // The EXTRA_DOCK_STATE that will be used if the extcon key-value pairs match
+ public final int extraStateValue;
+
+ // A list of key-value pairs that must be present in the extcon state for a match
+ // to be considered. An empty list is considered a matching wildcard.
+ public final List<Pair<String, String>> keyValuePairs = new ArrayList<>();
+
+ ExtconStateConfig(int extraStateValue) {
+ this.extraStateValue = extraStateValue;
+ }
+ }
+
+ private static List<ExtconStateConfig> loadExtconStateConfigs(Context context) {
+ String[] rows = context.getResources().getStringArray(
+ com.android.internal.R.array.config_dockExtconStateMapping);
+ try {
+ ArrayList<ExtconStateConfig> configs = new ArrayList<>();
+ for (String row : rows) {
+ String[] rowFields = row.split(",");
+ ExtconStateConfig config = new ExtconStateConfig(Integer.parseInt(rowFields[0]));
+ for (int i = 1; i < rowFields.length; i++) {
+ String[] keyValueFields = rowFields[i].split("=");
+ if (keyValueFields.length != 2) {
+ throw new IllegalArgumentException("Invalid key-value: " + rowFields[i]);
+ }
+ config.keyValuePairs.add(Pair.create(keyValueFields[0], keyValueFields[1]));
+ }
+ configs.add(config);
+ }
+ return configs;
+ } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
+ Slog.e(TAG, "Could not parse extcon state config", e);
+ return new ArrayList<>();
+ }
+ }
+
public DockObserver(Context context) {
super(context);
@@ -77,9 +165,25 @@
mAllowTheaterModeWakeFromDock = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
- init(); // set initial status
+ mExtconStateConfigs = loadExtconStateConfigs(context);
- mObserver.startObserving(DOCK_UEVENT_MATCH);
+ List<ExtconInfo> infos = ExtconInfo.getExtconInfoForTypes(new String[] {
+ ExtconInfo.EXTCON_DOCK
+ });
+
+ if (!infos.isEmpty()) {
+ ExtconInfo info = infos.get(0);
+ Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
+ + ", statePath: " + info.getStatePath());
+
+ // set initial status
+ setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+ mPreviousDockState = mActualDockState;
+
+ mExtconUEventObserver.startObserving(info);
+ } else {
+ Slog.i(TAG, "No extcon dock device found in this kernel.");
+ }
}
@Override
@@ -101,26 +205,6 @@
}
}
- private void init() {
- synchronized (mLock) {
- try {
- char[] buffer = new char[1024];
- FileReader file = new FileReader(DOCK_STATE_PATH);
- try {
- int len = file.read(buffer, 0, 1024);
- setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim()));
- mPreviousDockState = mActualDockState;
- } finally {
- file.close();
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have dock station support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- }
- }
-
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -234,19 +318,50 @@
}
};
- private final UEventObserver mObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Dock UEVENT: " + event.toString());
+ private int getDockedStateExtraValue(ExtconStateProvider state) {
+ for (ExtconStateConfig config : mExtconStateConfigs) {
+ boolean match = true;
+ for (Pair<String, String> keyValue : config.keyValuePairs) {
+ String stateValue = state.getValue(keyValue.first);
+ match = match && keyValue.second.equals(stateValue);
+ if (!match) {
+ break;
+ }
}
- try {
- synchronized (mLock) {
- setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ if (match) {
+ return config.extraStateValue;
+ }
+ }
+
+ return Intent.EXTRA_DOCK_STATE_DESK;
+ }
+
+ @VisibleForTesting
+ void setDockStateFromProviderForTesting(ExtconStateProvider provider) {
+ synchronized (mLock) {
+ setDockStateFromProviderLocked(provider);
+ }
+ }
+
+ private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
+ int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ if ("1".equals(provider.getValue("DOCK"))) {
+ state = getDockedStateExtraValue(provider);
+ }
+ setActualDockStateLocked(state);
+ }
+
+ private final ExtconUEventObserver mExtconUEventObserver = new ExtconUEventObserver() {
+ @Override
+ public void onUEvent(ExtconInfo extconInfo, UEventObserver.UEvent event) {
+ synchronized (mLock) {
+ String stateString = event.get("STATE");
+ if (stateString != null) {
+ setDockStateFromProviderLocked(ExtconStateProvider.fromString(stateString));
+ } else {
+ Slog.e(TAG, "Extcon event missing STATE: " + event);
}
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
};
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b5c0a67..9353dd8 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2721,7 +2721,8 @@
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
- String instanceName, String callingPackage, final int userId)
+ String instanceName, boolean isSupplementalProcessService, String callingPackage,
+ final int userId)
throws TransactionTooLargeException {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
@@ -2805,10 +2806,9 @@
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
- ServiceLookupResult res =
- retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
- callingPid, callingUid, userId, true,
- callerFg, isBindExternal, allowInstant);
+ ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
+ isSupplementalProcessService, resolvedType, callingPackage, callingPid, callingUid,
+ userId, true, callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
}
@@ -3228,6 +3228,20 @@
int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
boolean allowInstant) {
+ return retrieveServiceLocked(service, instanceName, false, resolvedType, callingPackage,
+ callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal,
+ allowInstant);
+ }
+
+ private ServiceLookupResult retrieveServiceLocked(Intent service,
+ String instanceName, boolean isSupplementalProcessService, String resolvedType,
+ String callingPackage, int callingPid, int callingUid, int userId,
+ boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
+ boolean allowInstant) {
+ if (isSupplementalProcessService && instanceName == null) {
+ throw new IllegalArgumentException("No instanceName provided for supplemental process");
+ }
+
ServiceRecord r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
@@ -3249,7 +3263,6 @@
if (instanceName == null) {
comp = service.getComponent();
} else {
- // This is for isolated services
final ComponentName realComp = service.getComponent();
if (realComp == null) {
throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
@@ -3304,12 +3317,19 @@
return null;
}
if (instanceName != null
- && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+ && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0
+ && !isSupplementalProcessService) {
throw new IllegalArgumentException("Can't use instance name '" + instanceName
- + "' with non-isolated service '" + sInfo.name + "'");
+ + "' with non-isolated non-supplemental service '" + sInfo.name + "'");
}
- ComponentName className = new ComponentName(
- sInfo.applicationInfo.packageName, sInfo.name);
+ if (isSupplementalProcessService
+ && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ throw new IllegalArgumentException("Service cannot be both supplemental and "
+ + "isolated");
+ }
+
+ ComponentName className = new ComponentName(sInfo.applicationInfo.packageName,
+ sInfo.name);
ComponentName name = comp != null ? comp : className;
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
name.getPackageName(), sInfo.applicationInfo.uid)) {
@@ -3392,7 +3412,8 @@
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
r = new ServiceRecord(mAm, className, name, definingPackageName,
- definingUid, filter, sInfo, callingFromFg, res);
+ definingUid, filter, sInfo, callingFromFg, res,
+ isSupplementalProcessService);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index 9a1bfdd..d9ee7d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -18,6 +18,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.TransactionTooLargeException;
/**
* Interface for in-process calls into
@@ -58,4 +61,24 @@
* @hide
*/
void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs);
+
+ /**
+ * Starts a supplemental process service and binds to it. You can through the arguments here
+ * have the system bring up multiple concurrent processes hosting their own instance of that
+ * service. The <var>userAppUid</var> you provide here identifies the different instances - each
+ * unique uid is attributed to a supplemental process.
+ *
+ * @param service Identifies the supplemental process service to connect to. The Intent must
+ * specify an explicit component name. This value cannot be null.
+ * @param conn Receives information as the service is started and stopped.
+ * This must be a valid ServiceConnection object; it must not be null.
+ * @param userAppUid Uid of the app for which the supplemental process needs to be spawned.
+ * @return {@code true} if the system is in the process of bringing up a
+ * service that your client has permission to bind to; {@code false}
+ * if the system couldn't find the service or if your client doesn't
+ * have permission to bind to it.
+ */
+ boolean startAndBindSupplementalProcessService(@NonNull Intent service,
+ @NonNull ServiceConnection conn, int userAppUid) throws TransactionTooLargeException;
+
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 902659c..442b9de 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,6 +218,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.LocusId;
+import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
@@ -256,6 +257,7 @@
import android.os.BugreportParams;
import android.os.Build;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.FactoryTest;
@@ -336,7 +338,7 @@
import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
@@ -4951,7 +4953,7 @@
// This line is needed to CTS test for the correct exception handling
// See b/138952436#comment36 for context
Slog.i(TAG, "About to commit checkpoint");
- IStorageManager storageManager = PackageHelper.getStorageManager();
+ IStorageManager storageManager = InstallLocationUtils.getStorageManager();
storageManager.commitChanges();
} catch (Exception e) {
PowerManager pm = (PowerManager)
@@ -12314,13 +12316,25 @@
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
- return bindIsolatedService(caller, token, service, resolvedType, connection, flags,
+ return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
null, callingPackage, userId);
}
- public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
+ /**
+ * Binds to a service with a given instanceName, creating it if it does not already exist.
+ * If the instanceName field is not supplied, binding to the service occurs as usual.
+ */
+ public int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
+ return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
+ instanceName, false, callingPackage, userId);
+ }
+
+ private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
+ String resolvedType, IServiceConnection connection, int flags, String instanceName,
+ boolean isSupplementalProcessService, String callingPackage, int userId)
+ throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
@@ -12332,6 +12346,10 @@
throw new IllegalArgumentException("callingPackage cannot be null");
}
+ if (isSupplementalProcessService && instanceName == null) {
+ throw new IllegalArgumentException("No instance name provided for isolated process");
+ }
+
// Ensure that instanceName, which is caller provided, does not contain
// unusual characters.
if (instanceName != null) {
@@ -12345,8 +12363,8 @@
}
synchronized(this) {
- return mServices.bindServiceLocked(caller, token, service,
- resolvedType, connection, flags, instanceName, callingPackage, userId);
+ return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
+ flags, instanceName, isSupplementalProcessService, callingPackage, userId);
}
}
@@ -15478,6 +15496,62 @@
}
}
+ /**
+ * Dump the resources structure for the given process
+ *
+ * @param process The process to dump resource info for
+ * @param fd The FileDescriptor to dump it into
+ * @throws RemoteException
+ */
+ public boolean dumpResources(String process, ParcelFileDescriptor fd, RemoteCallback callback)
+ throws RemoteException {
+ synchronized (this) {
+ ProcessRecord proc = findProcessLOSP(process, UserHandle.USER_CURRENT, "dumpResources");
+ IApplicationThread thread;
+ if (proc == null || (thread = proc.getThread()) == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+ thread.dumpResources(fd, callback);
+ return true;
+ }
+ }
+
+ /**
+ * Dump the resources structure for all processes
+ *
+ * @param fd The FileDescriptor to dump it into
+ * @throws RemoteException
+ */
+ public void dumpAllResources(ParcelFileDescriptor fd, PrintWriter pw) throws RemoteException {
+ synchronized (mProcLock) {
+ mProcessList.forEachLruProcessesLOSP(true, app -> {
+ ConditionVariable lock = new ConditionVariable();
+ RemoteCallback
+ finishCallback = new RemoteCallback(result -> lock.open(), null);
+
+ pw.println(String.format("------ DUMP RESOURCES %s (%s) ------",
+ app.processName,
+ app.info.packageName));
+ pw.flush();
+ try {
+ app.getThread().dumpResources(fd.dup(), finishCallback);
+ lock.block(2000);
+ } catch (Exception e) {
+ pw.println(String.format(
+ "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------",
+ app.processName,
+ app.info.packageName,
+ e.getMessage()));
+ pw.flush();
+ }
+ pw.println(String.format("------ END DUMP RESOURCES %s (%s) ------",
+ app.processName,
+ app.info.packageName));
+ pw.flush();
+ });
+ }
+ }
+
@Override
public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
String reportPackage) {
@@ -15823,6 +15897,34 @@
}
@Override
+ public boolean startAndBindSupplementalProcessService(Intent service,
+ ServiceConnection conn, int userAppUid) throws TransactionTooLargeException {
+ if (service == null) {
+ throw new IllegalArgumentException("intent is null");
+ }
+ if (conn == null) {
+ throw new IllegalArgumentException("connection is null");
+ }
+ if (service.getComponent() == null) {
+ throw new IllegalArgumentException("service must specify explicit component");
+ }
+ if (!UserHandle.isApp(userAppUid)) {
+ throw new IllegalArgumentException("uid is not within application range");
+ }
+
+ Handler handler = mContext.getMainThreadHandler();
+ int flags = Context.BIND_AUTO_CREATE;
+
+ final IServiceConnection sd = mContext.getServiceDispatcher(conn, handler, flags);
+ service.prepareToLeaveProcess(mContext);
+ return ActivityManagerService.this.bindServiceInstance(
+ mContext.getIApplicationThread(), mContext.getActivityToken(), service,
+ service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags,
+ Integer.toString(userAppUid), /*isSupplementalProcessService*/ true,
+ mContext.getOpPackageName(), UserHandle.getUserId(userAppUid)) != 0;
+ }
+
+ @Override
public void onUserRemoved(@UserIdInt int userId) {
// Clean up any ActivityTaskManager state (by telling it the user is stopped)
mAtmInternal.onUserStopped(userId);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 24e7ba4..da78e2d 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -570,6 +570,14 @@
ComponentName instanceName, String definingPackageName, int definingUid,
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Runnable restarter) {
+ this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg,
+ restarter, false);
+ }
+
+ ServiceRecord(ActivityManagerService ams, ComponentName name,
+ ComponentName instanceName, String definingPackageName, int definingUid,
+ Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
+ Runnable restarter, boolean isSupplementalProcessService) {
this.ams = ams;
this.name = name;
this.instanceName = instanceName;
@@ -580,7 +588,8 @@
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
- if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ || isSupplementalProcessService) {
processName = sInfo.processName + ":" + instanceName.getClassName();
} else {
processName = sInfo.processName;
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index ec37134..af1dd33 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -98,7 +98,10 @@
private final IGameServiceController mGameServiceController =
new IGameServiceController.Stub() {
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY)
public void createGameSession(int taskId) {
+ mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY,
+ "createGameSession()");
mBackgroundExecutor.execute(() -> {
GameServiceProviderInstanceImpl.this.createGameSession(taskId);
});
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 0da6a1b..79705a3 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -53,6 +53,7 @@
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -64,6 +65,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -681,16 +683,18 @@
+ ", Latency: " + latency);
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
+ final OperationContext operationContext = new OperationContext();
+ operationContext.isCrypto = isCrypto();
+ BiometricFrameworkStatsLogger.getInstance().authenticate(
+ operationContext,
statsModality(),
- mUserId,
- isCrypto(),
+ BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mPreAuthInfo.confirmationRequested,
- FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
- latency,
mDebugEnabled,
- -1 /* sensorId */,
+ latency,
+ FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
+ mPreAuthInfo.confirmationRequested,
+ mUserId,
-1f /* ambientLightLux */);
} else {
final long latency = System.currentTimeMillis() - mStartTimeMs;
@@ -711,17 +715,18 @@
+ ", Latency: " + latency);
}
// Auth canceled
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
+ final OperationContext operationContext = new OperationContext();
+ operationContext.isCrypto = isCrypto();
+ BiometricFrameworkStatsLogger.getInstance().error(
+ operationContext,
statsModality(),
- mUserId,
- isCrypto(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- error,
- 0 /* vendorCode */,
mDebugEnabled,
latency,
- -1 /* sensorId */);
+ error,
+ 0 /* vendorCode */,
+ mUserId);
}
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index 8d28298..c5e266f 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -17,6 +17,8 @@
package com.android.server.biometrics.log;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
import android.hardware.biometrics.common.OperationContext;
import java.util.function.Consumer;
@@ -26,11 +28,21 @@
* logging or optimizations.
*/
public interface BiometricContext {
- /** Gets the context source. */
- static BiometricContext getInstance() {
- return BiometricContextProvider.sInstance.get();
+ /** Gets the context source from the system context. */
+ static BiometricContext getInstance(@NonNull Context context) {
+ return BiometricContextProvider.defaultProvider(context);
}
+ /** Update the given context with the most recent values and return it. */
+ OperationContext updateContext(@NonNull OperationContext operationContext,
+ boolean isCryptoOperation);
+
+ /** The session id for keyguard entry, if active, or null. */
+ @Nullable Integer getKeyguardEntrySessionId();
+
+ /** The session id for biometric prompt usage, if active, or null. */
+ @Nullable Integer getBiometricPromptSessionId();
+
/** If the display is in AOD. */
boolean isAoD();
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 65e9e3d..70acaff 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -18,16 +18,22 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.StatusBarManager;
import android.content.Context;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Singleton;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
import java.util.Map;
@@ -41,22 +47,41 @@
private static final String TAG = "BiometricContextProvider";
- static final Singleton<BiometricContextProvider> sInstance =
- new Singleton<BiometricContextProvider>() {
- @Override
- protected BiometricContextProvider create() {
- return new BiometricContextProvider(IStatusBarService.Stub.asInterface(
- ServiceManager.getService(
+ private static final int SESSION_TYPES =
+ StatusBarManager.SESSION_KEYGUARD | StatusBarManager.SESSION_BIOMETRIC_PROMPT;
+
+ private static BiometricContextProvider sInstance;
+
+ static BiometricContextProvider defaultProvider(@NonNull Context context) {
+ synchronized (BiometricContextProvider.class) {
+ if (sInstance == null) {
+ try {
+ sInstance = new BiometricContextProvider(
+ new AmbientDisplayConfiguration(context),
+ IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.STATUS_BAR_SERVICE)), null /* handler */);
+ } catch (ServiceNotFoundException e) {
+ throw new IllegalStateException("Failed to find required service", e);
}
- };
+ }
+ }
+ return sInstance;
+ }
@NonNull
private final Map<OperationContext, Consumer<OperationContext>> mSubscribers =
new ConcurrentHashMap<>();
+ @Nullable
+ private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();
+
+ private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ private boolean mIsDozing = false;
+
@VisibleForTesting
- BiometricContextProvider(@NonNull IStatusBarService service, @Nullable Handler handler) {
+ BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
+ @NonNull IStatusBarService service, @Nullable Handler handler) {
+ mAmbientDisplayConfiguration = ambientDisplayConfiguration;
try {
service.setBiometicContextListener(new IBiometricContextListener.Stub() {
@Override
@@ -73,16 +98,70 @@
}
}
});
+ service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
+ @Override
+ public void onSessionStarted(int sessionType, InstanceId instance) {
+ mSession.put(sessionType, instance);
+ }
+
+ @Override
+ public void onSessionEnded(int sessionType, InstanceId instance) {
+ final InstanceId id = mSession.remove(sessionType);
+ if (id != null && instance != null && id.getId() != instance.getId()) {
+ Slog.w(TAG, "session id mismatch");
+ }
+ }
+ });
} catch (RemoteException e) {
Slog.e(TAG, "Unable to register biometric context listener", e);
}
}
- private boolean mIsDozing = false;
+ @Override
+ public OperationContext updateContext(@NonNull OperationContext operationContext,
+ boolean isCryptoOperation) {
+ operationContext.isAoD = isAoD();
+ operationContext.isCrypto = isCryptoOperation;
+ setFirstSessionId(operationContext);
+ return operationContext;
+ }
+
+ private void setFirstSessionId(@NonNull OperationContext operationContext) {
+ Integer sessionId = getKeyguardEntrySessionId();
+ if (sessionId != null) {
+ operationContext.id = sessionId;
+ operationContext.reason = OperationReason.KEYGUARD;
+ return;
+ }
+
+ sessionId = getBiometricPromptSessionId();
+ if (sessionId != null) {
+ operationContext.id = sessionId;
+ operationContext.reason = OperationReason.BIOMETRIC_PROMPT;
+ return;
+ }
+
+ operationContext.id = 0;
+ operationContext.reason = OperationReason.UNKNOWN;
+ }
+
+ @Nullable
+ @Override
+ public Integer getKeyguardEntrySessionId() {
+ final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD);
+ return id != null ? id.getId() : null;
+ }
+
+ @Nullable
+ @Override
+ public Integer getBiometricPromptSessionId() {
+ final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT);
+ return id != null ? id.getId() : null;
+ }
@Override
public boolean isAoD() {
- return mIsDozing;
+ return mIsDozing && mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
}
@Override
@@ -98,7 +177,7 @@
private void notifySubscribers() {
mSubscribers.forEach((context, consumer) -> {
- context.isAoD = mIsDozing;
+ context.isAoD = isAoD();
consumer.accept(context);
});
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index c3471bd..8965227 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -17,6 +17,8 @@
package com.android.server.biometrics.log;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
@@ -33,42 +35,49 @@
private BiometricFrameworkStatsLogger() {}
+ /** Shared instance. */
public static BiometricFrameworkStatsLogger getInstance() {
return sInstance;
}
/** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
- public void acquired(
+ public void acquired(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug,
- int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+ int acquiredInfo, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsAction,
statsClient,
acquiredInfo,
vendorCode,
isDebug,
- -1 /* sensorId */);
+ -1 /* sensorId */,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
- public void authenticate(
+ public void authenticate(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
- boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
- int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
+ int authState, boolean requireConfirmation,
+ int targetUserId, float ambientLightLux) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsClient,
requireConfirmation,
authState,
sanitizeLatency(latency),
isDebug,
-1 /* sensorId */,
- ambientLightLux);
+ ambientLightLux,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
@@ -84,20 +93,23 @@
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
- public void error(
+ public void error(OperationContext operationContext,
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
- int error, int vendorCode, boolean isCrypto, int targetUserId) {
+ int error, int vendorCode, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
statsModality,
targetUserId,
- isCrypto,
+ operationContext.isCrypto,
statsAction,
statsClient,
error,
vendorCode,
isDebug,
sanitizeLatency(latency),
- -1 /* sensorId */);
+ -1 /* sensorId */,
+ operationContext.id,
+ sessionType(operationContext.reason),
+ operationContext.isAoD);
}
/** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
@@ -123,4 +135,14 @@
}
return latency;
}
+
+ private static int sessionType(@OperationReason byte reason) {
+ if (reason == OperationReason.BIOMETRIC_PROMPT) {
+ return BiometricsProtoEnums.SESSION_TYPE_BIOMETRIC_PROMPT;
+ }
+ if (reason == OperationReason.KEYGUARD) {
+ return BiometricsProtoEnums.SESSION_TYPE_KEYGUARD_ENTRY;
+ }
+ return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index 0188390..2a8d9f1 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -25,6 +25,7 @@
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
@@ -144,8 +145,8 @@
}
/** Log an acquisition event. */
- public void logOnAcquired(Context context,
- int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+ public void logOnAcquired(Context context, OperationContext operationContext,
+ int acquiredInfo, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -165,7 +166,7 @@
if (DEBUG) {
Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", AcquiredInfo: " + acquiredInfo
@@ -176,14 +177,14 @@
return;
}
- mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
+ mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- acquiredInfo, vendorCode, isCrypto, targetUserId);
+ acquiredInfo, vendorCode, targetUserId);
}
/** Log an error during an operation. */
- public void logOnError(Context context,
- int error, int vendorCode, boolean isCrypto, int targetUserId) {
+ public void logOnError(Context context, OperationContext operationContext,
+ int error, int vendorCode, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -194,7 +195,7 @@
if (DEBUG) {
Slog.v(TAG, "Error! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", Error: " + error
@@ -208,14 +209,14 @@
return;
}
- mSink.error(mStatsModality, mStatsAction, mStatsClient,
+ mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId), latency,
- error, vendorCode, isCrypto, targetUserId);
+ error, vendorCode, targetUserId);
}
/** Log authentication attempt. */
- public void logOnAuthenticated(Context context,
- boolean authenticated, boolean requireConfirmation, boolean isCrypto,
+ public void logOnAuthenticated(Context context, OperationContext operationContext,
+ boolean authenticated, boolean requireConfirmation,
int targetUserId, boolean isBiometricPrompt) {
if (!mShouldLogMetrics) {
return;
@@ -241,7 +242,7 @@
if (DEBUG) {
Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCrypto
+ + ", IsCrypto: " + operationContext.isCrypto
+ ", Client: " + mStatsClient
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
@@ -255,10 +256,9 @@
return;
}
- mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
+ mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- latency, authenticated, authState, requireConfirmation, isCrypto,
- targetUserId, isBiometricPrompt, mLastAmbientLux);
+ latency, authState, requireConfirmation, targetUserId, mLastAmbientLux);
}
/** Log enrollment outcome. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index e07a68c..0f0032b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -110,8 +110,8 @@
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
if (mShouldSendErrorToClient) {
- getLogger().logOnError(getContext(), errorCode, vendorCode,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ errorCode, vendorCode, getTargetUserId());
try {
if (getListener() != null) {
mShouldSendErrorToClient = false;
@@ -169,8 +169,8 @@
protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
boolean shouldSend) {
- getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnAcquired(getContext(), getOperationContext(),
+ acquiredInfo, vendorCode, getTargetUserId());
if (DEBUG) {
Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
+ ", shouldSend: " + shouldSend);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 949edd0..54b79e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -166,8 +166,8 @@
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
- getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
- isCryptoOperation(), getTargetUserId(), isBiometricPrompt());
+ getLogger().logOnAuthenticated(getContext(), getOperationContext(),
+ authenticated, mRequireConfirmation, getTargetUserId(), isBiometricPrompt());
final ClientMonitorCallbackConverter listener = getListener();
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index eabafce..a6e8911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -37,7 +37,7 @@
protected final Supplier<T> mLazyDaemon;
@NonNull
- protected final OperationContext mOperationContext = new OperationContext();
+ private final OperationContext mOperationContext = new OperationContext();
/**
* @param context system_server context
@@ -82,6 +82,23 @@
super.destroy();
// subclasses should do this earlier in most cases, but ensure it happens now
+ unsubscribeBiometricContext();
+ }
+
+ protected OperationContext getOperationContext() {
+ return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
+ }
+
+ protected ClientMonitorCallback getBiometricContextUnsubscriber() {
+ return new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor monitor, boolean success) {
+ unsubscribeBiometricContext();
+ }
+ };
+ }
+
+ protected void unsubscribeBiometricContext() {
getBiometricContext().unsubscribe(mOperationContext);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 039b08e..2e82057 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -57,6 +57,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -645,7 +646,7 @@
try {
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(), props, instance,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, BiometricContext.getInstance(getContext()));
mServiceProviders.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 75a1e0c..7765ab3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -26,8 +26,6 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceManager;
@@ -157,13 +155,8 @@
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = getBiometricContext().isAoD();
- context.isCrypto = isCryptoOperation();
- return session.getSession().authenticateWithContext(mOperationId, context);
+ return session.getSession().authenticateWithContext(
+ mOperationId, getOperationContext());
} else {
return session.getSession().authenticate(mOperationId);
}
@@ -282,8 +275,8 @@
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -298,8 +291,8 @@
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index c79e601..efedcf8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -22,8 +22,6 @@
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -116,13 +114,7 @@
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = getBiometricContext().isAoD();
- context.isCrypto = isCryptoOperation();
- return session.getSession().detectInteractionWithContext(context);
+ return session.getSession().detectInteractionWithContext(getOperationContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 6f6dadc8..da78536 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -21,8 +21,6 @@
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.face.EnrollmentType;
import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
@@ -200,14 +198,8 @@
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = getBiometricContext().isAoD();
- context.isCrypto = isCryptoOperation();
return session.getSession().enrollWithContext(
- hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, context);
+ hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, getOperationContext());
} else {
return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features,
mHwPreviewHandle);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 64b0892..4e03ee9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -90,7 +90,7 @@
@NonNull private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
-
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IFace mDaemon;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -128,7 +128,8 @@
public FaceProvider(@NonNull Context context, @NonNull SensorProps[] props,
@NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mHalInstanceName = halInstanceName;
mSensors = new SparseArray<>();
@@ -137,6 +138,7 @@
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ mBiometricContext = biometricContext;
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -156,7 +158,7 @@
prop.supportsDetectInteraction, prop.halControlsPreview,
false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher);
+ internalProp, lockoutResetDispatcher, mBiometricContext);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
@@ -242,7 +244,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
@@ -253,7 +255,8 @@
mHandler.post(() -> {
final InvalidationRequesterClient<Face> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext),
+ mBiometricContext,
FaceUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -294,7 +297,7 @@
mSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
@@ -324,7 +327,7 @@
new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -337,7 +340,7 @@
mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), challenge);
+ mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -358,7 +361,7 @@
ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), maxTemplatesPerUser, debugConsent);
+ mBiometricContext, maxTemplatesPerUser, debugConsent);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -391,7 +394,7 @@
mSensors.get(sensorId).getLazySession(),
token, id, callback, userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), isStrongBiometric);
+ mBiometricContext, isStrongBiometric);
scheduleForSensor(sensorId, client);
});
@@ -416,7 +419,7 @@
userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), isStrongBiometric,
+ mBiometricContext, isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, isKeyguardBypassEnabled);
scheduleForSensor(sensorId, client);
@@ -472,7 +475,7 @@
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -486,7 +489,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), hardwareAuthToken,
+ mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
@@ -508,7 +511,7 @@
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId,
mContext.getOpPackageName(), sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
feature, enabled, hardwareAuthToken);
scheduleForSensor(sensorId, client);
});
@@ -527,7 +530,7 @@
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mSensors.get(sensorId).getLazySession(), token, callback, userId,
mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext),
- BiometricContext.getInstance());
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -550,7 +553,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), enrolledList,
+ mBiometricContext, enrolledList,
FaceUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, callback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index fa07d12..b69c760 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -476,7 +476,8 @@
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -492,7 +493,7 @@
public StopUserClient<?> getStopUserClient(int userId) {
return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), biometricContext,
() -> mCurrentSession = null);
}
@@ -529,7 +530,7 @@
return new FaceStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), biometricContext,
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index be1ed7d..73c759f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -119,6 +119,7 @@
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
+ @NonNull private final BiometricContext mBiometricContext;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -155,6 +156,7 @@
@NonNull private final LockoutHalImpl mLockoutTracker;
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+
HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
@NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
@@ -337,12 +339,14 @@
@NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull Handler handler,
- @NonNull BiometricScheduler scheduler) {
+ @NonNull BiometricScheduler scheduler,
+ @NonNull BiometricContext biometricContext) {
mSensorProperties = sensorProps;
mContext = context;
mSensorId = sensorProps.sensorId;
mScheduler = scheduler;
mHandler = handler;
+ mBiometricContext = biometricContext;
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
@@ -367,7 +371,8 @@
final Handler handler = new Handler(Looper.getMainLooper());
return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
- null /* gestureAvailabilityTracker */));
+ null /* gestureAvailabilityTracker */),
+ BiometricContext.getInstance(context));
}
@Override
@@ -538,7 +543,7 @@
opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), sSystemClock.millis());
+ mBiometricContext, sSystemClock.millis());
mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -570,7 +575,7 @@
mLazyDaemon, token, userId, opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -601,7 +606,7 @@
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -649,8 +654,8 @@
mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
opPackageName, cookie, false /* requireConfirmation */, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), isStrongBiometric, mLockoutTracker, mUsageStats,
- allowBackgroundAuthentication, isKeyguardBypassEnabled);
+ mBiometricContext, isStrongBiometric, mLockoutTracker,
+ mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -685,7 +690,7 @@
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), mAuthenticatorIds);
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -703,7 +708,7 @@
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), mAuthenticatorIds);
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -722,7 +727,7 @@
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), hardwareAuthToken);
+ mBiometricContext, hardwareAuthToken);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -744,7 +749,8 @@
final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext),
- BiometricContext.getInstance(), feature, enabled, hardwareAuthToken, faceId);
+ mBiometricContext,
+ feature, enabled, hardwareAuthToken, faceId);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -764,7 +770,7 @@
final int faceId = faces.get(0).getBiometricId();
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
token, listener, userId, opPackageName, mSensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
feature, faceId);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -793,7 +799,7 @@
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), enrolledList,
+ mBiometricContext, enrolledList,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
@@ -918,7 +924,7 @@
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), hasEnrolled, mAuthenticatorIds);
+ mBiometricContext, hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 6366e19..b4befd2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -83,6 +83,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -815,7 +816,8 @@
UserHandle.USER_CURRENT) != 0) {
fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
mFingerprintStateCallback, hidlSensor,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
+ BiometricContext.getInstance(getContext()));
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
mFingerprintStateCallback, hidlSensor, mHandler,
@@ -843,7 +845,8 @@
final FingerprintProvider provider =
new FingerprintProvider(getContext(), mFingerprintStateCallback, props,
instance, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher);
+ mGestureAvailabilityDispatcher,
+ BiometricContext.getInstance(getContext()));
mServiceProviders.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 184e1ea..d26a780 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -24,7 +24,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationReason;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -108,7 +108,8 @@
@NonNull
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
+ return new ClientMonitorCompositeCallback(mALSProbeCallback,
+ getBiometricContextUnsubscriber(), callback);
}
@Override
@@ -178,12 +179,17 @@
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- // TODO: add reason, id
- mOperationContext.id = 0;
- mOperationContext.reason = OperationReason.UNKNOWN;
- mOperationContext.isAoD = getBiometricContext().isAoD();
- mOperationContext.isCrypto = isCryptoOperation();
- return session.getSession().authenticateWithContext(mOperationId, mOperationContext);
+ final OperationContext opContext = getOperationContext();
+ final ICancellationSignal cancel = session.getSession().authenticateWithContext(
+ mOperationId, opContext);
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ });
+ return cancel;
} else {
return session.getSession().authenticate(mOperationId);
}
@@ -192,6 +198,8 @@
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
+ unsubscribeBiometricContext();
+
if (mCancellationSignal != null) {
try {
mCancellationSignal.cancel();
@@ -221,7 +229,7 @@
context.y = y;
context.minor = minor;
context.major = major;
- context.isAoD = false; // TODO; get value
+ context.isAoD = getBiometricContext().isAoD();
session.getSession().onPointerDownWithContext(context);
} else {
session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major);
@@ -279,8 +287,8 @@
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -298,8 +306,8 @@
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
- isCryptoOperation(), getTargetUserId());
+ getLogger().logOnError(getContext(), getOperationContext(),
+ error, 0 /* vendorCode */, getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 9d348e1..0e89814 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -21,8 +21,6 @@
import android.content.Context;
import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -99,13 +97,7 @@
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = getBiometricContext().isAoD();
- context.isCrypto = isCryptoOperation();
- return session.getSession().detectInteractionWithContext(context);
+ return session.getSession().detectInteractionWithContext(getOperationContext());
} else {
return session.getSession().detectInteraction();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ed16a6d..e21d901 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -24,7 +24,6 @@
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
-import android.hardware.biometrics.common.OperationReason;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -39,6 +38,8 @@
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -58,6 +59,7 @@
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final SensorOverlays mSensorOverlays;
+ @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
@@ -83,6 +85,8 @@
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
+ mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
@@ -92,8 +96,8 @@
@NonNull
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(
- getLogger().createALSCallback(true /* startWithClient */), callback);
+ return new ClientMonitorCompositeCallback(mALSProbeCallback,
+ getBiometricContextUnsubscriber(), callback);
}
@Override
@@ -142,22 +146,6 @@
}
@Override
- protected void stopHalOperation() {
- mSensorOverlays.hide(getSensorId());
-
- if (mCancellationSignal != null) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
- mCallback.onClientFinished(this, false /* success */);
- }
- }
- }
-
- @Override
protected void startHalOperation() {
mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
@@ -178,22 +166,44 @@
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
if (session.hasContextMethods()) {
- final OperationContext context = new OperationContext();
- // TODO: add reason, id
- context.id = 0;
- context.reason = OperationReason.UNKNOWN;
- context.isAoD = getBiometricContext().isAoD();
- context.isCrypto = isCryptoOperation();
- return session.getSession().enrollWithContext(hat, context);
+ final OperationContext opContext = getOperationContext();
+ final ICancellationSignal cancel = session.getSession().enrollWithContext(
+ hat, opContext);
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ });
+ return cancel;
} else {
return session.getSession().enroll(hat);
}
}
@Override
+ protected void stopHalOperation() {
+ mSensorOverlays.hide(getSensorId());
+ unsubscribeBiometricContext();
+
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+ }
+
+ @Override
public void onPointerDown(int x, int y, float minor, float major) {
try {
mIsPointerDown = true;
+ mALSProbeCallback.getProbe().enable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
@@ -203,7 +213,7 @@
context.y = y;
context.minor = minor;
context.major = major;
- context.isAoD = false;
+ context.isAoD = getBiometricContext().isAoD();
session.getSession().onPointerDownWithContext(context);
} else {
session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major);
@@ -217,6 +227,7 @@
public void onPointerUp() {
try {
mIsPointerDown = false;
+ mALSProbeCallback.getProbe().disable();
final AidlSession session = getFreshDaemon();
if (session.hasContextMethods()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 221ff941..f810bca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -101,7 +101,7 @@
@NonNull private final BiometricTaskStackListener mTaskStackListener;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
-
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
@@ -144,7 +144,8 @@
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mFingerprintStateCallback = fingerprintStateCallback;
mHalInstanceName = halInstanceName;
@@ -153,6 +154,7 @@
mLockoutResetDispatcher = lockoutResetDispatcher;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ mBiometricContext = biometricContext;
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
@@ -184,7 +186,8 @@
location.sensorRadius))
.collect(Collectors.toList()));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
+ internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
@@ -303,7 +306,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
});
@@ -313,7 +316,8 @@
mHandler.post(() -> {
final InvalidationRequesterClient<Fingerprint> client =
new InvalidationRequesterClient<>(mContext, userId, sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext),
+ mBiometricContext,
FingerprintUtils.getInstance(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -327,7 +331,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), hardwareAuthToken,
+ mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
scheduleForSensor(sensorId, client);
});
@@ -343,7 +347,7 @@
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
scheduleForSensor(sensorId, client);
});
}
@@ -358,7 +362,7 @@
userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), challenge);
+ mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
}
@@ -378,7 +382,7 @@
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@@ -419,7 +423,7 @@
mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(),
+ mBiometricContext,
mUdfpsOverlayController, isStrongBiometric);
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -439,7 +443,7 @@
userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), isStrongBiometric,
+ mBiometricContext, isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
@@ -503,7 +507,7 @@
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -520,7 +524,7 @@
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
enrolledList, FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
@@ -559,7 +563,7 @@
mSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 27226b3..63e345e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -429,7 +429,8 @@
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
mTag = tag;
mProvider = provider;
mContext = context;
@@ -447,7 +448,7 @@
public StopUserClient<?> getStopUserClient(int userId) {
return new FingerprintStopUserClient(mContext, mLazySession, mToken,
userId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), biometricContext,
() -> mCurrentSession = null);
}
@@ -484,7 +485,7 @@
return new FingerprintStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(),
+ BiometricLogger.ofUnknown(mContext), biometricContext,
resultController, userStartedCallback);
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 6c35c8c..9d60859 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -120,6 +120,7 @@
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
+ @NonNull private final BiometricContext mBiometricContext;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -320,15 +321,18 @@
}
}
+ @VisibleForTesting
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller) {
+ @NonNull HalResultController controller,
+ @NonNull BiometricContext biometricContext) {
mContext = context;
mFingerprintStateCallback = fingerprintStateCallback;
+ mBiometricContext = biometricContext;
mSensorProperties = sensorProps;
mSensorId = sensorProps.sensorId;
@@ -370,7 +374,7 @@
final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, BiometricContext.getInstance(context));
}
@Override
@@ -497,7 +501,7 @@
mContext.getOpPackageName(), mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -544,7 +548,7 @@
userId, mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), mLockoutTracker);
+ mBiometricContext, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -559,7 +563,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -573,7 +577,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance());
+ mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -594,7 +598,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(),
+ mBiometricContext,
mUdfpsOverlayController, mSidefpsController,
enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -636,7 +640,8 @@
mLazyDaemon, token, id, listener, userId, opPackageName,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), mUdfpsOverlayController, isStrongBiometric);
+ mBiometricContext, mUdfpsOverlayController,
+ isStrongBiometric);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
@@ -657,7 +662,7 @@
restricted, opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
- BiometricContext.getInstance(), isStrongBiometric,
+ mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
@@ -702,7 +707,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), mAuthenticatorIds);
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -722,7 +727,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), mAuthenticatorIds);
+ mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@@ -739,7 +744,7 @@
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
- BiometricContext.getInstance(), enrolledList,
+ mBiometricContext, enrolledList,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 1694bd9..149526f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -37,6 +37,7 @@
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -247,7 +248,8 @@
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
Slog.d(TAG, "Creating Fingerprint23Mock!");
final Handler handler = new Handler(Looper.getMainLooper());
@@ -256,7 +258,7 @@
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
- handler, lockoutResetDispatcher, controller);
+ handler, lockoutResetDispatcher, controller, biometricContext);
}
private static abstract class FakeFingerRunnable implements Runnable {
@@ -385,9 +387,10 @@
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull MockHalResultController controller) {
+ @NonNull MockHalResultController controller,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, biometricContext);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index f10d4e4..c2929d0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -130,8 +130,9 @@
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
ArrayList<Byte> hardwareAuthToken) {
- getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
- isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */);
+ getLogger().logOnAuthenticated(getContext(), getOperationContext(),
+ authenticated, false /* requireConfirmation */,
+ getTargetUserId(), false /* isBiometricPrompt */);
// Do not distinguish between success/failures.
vibrateSuccess();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1f44854..3494342 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1014,6 +1014,9 @@
mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
}
}
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
+ }
}
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 151ec81..fb36dc7 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -44,26 +44,18 @@
AmbientSensor.AmbientBrightnessSensor.Callbacks,
AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
- protected static final String TAG = "DisplayWhiteBalanceController";
- protected boolean mLoggingEnabled;
+ private static final String TAG = "DisplayWhiteBalanceController";
+ private boolean mLoggingEnabled;
- private boolean mEnabled;
+ private final ColorDisplayServiceInternal mColorDisplayServiceInternal;
- // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
- // implements Callbacks and passes itself to the DWBC so it can call back into it without
- // knowing about it.
- private Callbacks mCallbacks;
-
- private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
-
+ private final AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
@VisibleForTesting
AmbientFilter mBrightnessFilter;
- private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
-
+ private final AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
@VisibleForTesting
AmbientFilter mColorTemperatureFilter;
- private DisplayWhiteBalanceThrottler mThrottler;
-
+ private final DisplayWhiteBalanceThrottler mThrottler;
// In low brightness conditions the ALS readings are more noisy and produce
// high errors. This default is introduced to provide a fixed display color
// temperature when sensor readings become unreliable.
@@ -74,16 +66,12 @@
private final float mHighLightAmbientColorTemperature;
private float mAmbientColorTemperature;
-
@VisibleForTesting
float mPendingAmbientColorTemperature;
private float mLastAmbientColorTemperature;
- private ColorDisplayServiceInternal mColorDisplayServiceInternal;
-
// The most recent ambient color temperature values are kept for debugging purposes.
- private static final int HISTORY_SIZE = 50;
- private History mAmbientColorTemperatureHistory;
+ private final History mAmbientColorTemperatureHistory;
// Override the ambient color temperature for debugging purposes.
private float mAmbientColorTemperatureOverride;
@@ -91,6 +79,10 @@
// A piecewise linear relationship between ambient and display color temperatures.
private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
+ // A piecewise linear relationship between ambient and display color temperatures, with a
+ // stronger change between the two sets of values.
+ private Spline.LinearSpline mStrongAmbientToDisplayColorTemperatureSpline;
+
// In very low or very high brightness conditions Display White Balance should
// be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline.
// However, setting Display White Balance based on thresholds can cause the
@@ -109,6 +101,17 @@
private float mLatestLowLightBias;
private float mLatestHighLightBias;
+ private boolean mEnabled;
+
+ // Whether a higher-strength adjustment should be applied; this must be enabled in addition to
+ // mEnabled in order to be applied.
+ private boolean mStrongModeEnabled;
+
+ // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
+ // implements Callbacks and passes itself to the DWBC so it can call back into it without
+ // knowing about it.
+ private Callbacks mDisplayPowerControllerCallbacks;
+
/**
* @param brightnessSensor
* The sensor used to detect changes in the ambient brightness.
@@ -159,16 +162,18 @@
@NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
@NonNull AmbientFilter colorTemperatureFilter,
@NonNull DisplayWhiteBalanceThrottler throttler,
- float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases,
+ float[] lowLightAmbientBrightnesses,
+ float[] lowLightAmbientBiases,
float lowLightAmbientColorTemperature,
- float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases,
+ float[] highLightAmbientBrightnesses,
+ float[] highLightAmbientBiases,
float highLightAmbientColorTemperature,
- float[] ambientColorTemperatures, float[] displayColorTemperatures) {
+ float[] ambientColorTemperatures,
+ float[] displayColorTemperatures,
+ float[] strongAmbientColorTemperatures,
+ float[] strongDisplayColorTemperatures) {
validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
colorTemperatureFilter, throttler);
- mLoggingEnabled = false;
- mEnabled = false;
- mCallbacks = null;
mBrightnessSensor = brightnessSensor;
mBrightnessFilter = brightnessFilter;
mColorTemperatureSensor = colorTemperatureSensor;
@@ -179,7 +184,7 @@
mAmbientColorTemperature = -1.0f;
mPendingAmbientColorTemperature = -1.0f;
mLastAmbientColorTemperature = -1.0f;
- mAmbientColorTemperatureHistory = new History(HISTORY_SIZE);
+ mAmbientColorTemperatureHistory = new History(/* size= */ 50);
mAmbientColorTemperatureOverride = -1.0f;
try {
@@ -235,6 +240,13 @@
mAmbientToDisplayColorTemperatureSpline = null;
}
+ try {
+ mStrongAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+ strongAmbientColorTemperatures, strongDisplayColorTemperatures);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to create strong ambient to display color temperature spline", e);
+ }
+
mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
}
@@ -255,6 +267,19 @@
}
/**
+ * Enable/disable the stronger adjustment option.
+ *
+ * @param enabled whether the stronger adjustment option should be turned on
+ */
+ public void setStrongModeEnabled(boolean enabled) {
+ mStrongModeEnabled = enabled;
+ if (mEnabled) {
+ updateAmbientColorTemperature();
+ updateDisplayColorTemperature();
+ }
+ }
+
+ /**
* Set an object to call back to when the display color temperature should be updated.
*
* @param callbacks
@@ -263,10 +288,10 @@
* @return Whether the method succeeded or not.
*/
public boolean setCallbacks(Callbacks callbacks) {
- if (mCallbacks == callbacks) {
+ if (mDisplayPowerControllerCallbacks == callbacks) {
return false;
}
- mCallbacks = callbacks;
+ mDisplayPowerControllerCallbacks = callbacks;
return true;
}
@@ -321,7 +346,7 @@
writer.println("DisplayWhiteBalanceController");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mEnabled=" + mEnabled);
- writer.println(" mCallbacks=" + mCallbacks);
+ writer.println(" mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
mBrightnessSensor.dump(writer);
mBrightnessFilter.dump(writer);
mColorTemperatureSensor.dump(writer);
@@ -336,6 +361,8 @@
writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
writer.println(" mAmbientToDisplayColorTemperatureSpline="
+ mAmbientToDisplayColorTemperatureSpline);
+ writer.println(" mStrongAmbientToDisplayColorTemperatureSpline="
+ + mStrongAmbientToDisplayColorTemperatureSpline);
writer.println(" mLowLightAmbientBrightnessToBiasSpline="
+ mLowLightAmbientBrightnessToBiasSpline);
writer.println(" mHighLightAmbientBrightnessToBiasSpline="
@@ -364,9 +391,20 @@
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
mLatestAmbientColorTemperature = ambientColorTemperature;
- if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) {
- ambientColorTemperature =
- mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
+ if (mStrongModeEnabled) {
+ if (mStrongAmbientToDisplayColorTemperatureSpline != null
+ && ambientColorTemperature != -1.0f) {
+ ambientColorTemperature =
+ mStrongAmbientToDisplayColorTemperatureSpline.interpolate(
+ ambientColorTemperature);
+ }
+ } else {
+ if (mAmbientToDisplayColorTemperatureSpline != null
+ && ambientColorTemperature != -1.0f) {
+ ambientColorTemperature =
+ mAmbientToDisplayColorTemperatureSpline.interpolate(
+ ambientColorTemperature);
+ }
}
float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -409,8 +447,8 @@
Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
}
mPendingAmbientColorTemperature = ambientColorTemperature;
- if (mCallbacks != null) {
- mCallbacks.updateWhiteBalance();
+ if (mDisplayPowerControllerCallbacks != null) {
+ mDisplayPowerControllerCallbacks.updateWhiteBalance();
}
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index a72b1ed..07821b0 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -87,15 +87,22 @@
.config_displayWhiteBalanceHighLightAmbientColorTemperature);
final float[] ambientColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
- final float[] displayColorTempeartures = getFloatArray(resources,
+ final float[] displayColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
+ final float[] strongAmbientColorTemperatures = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceStrongAmbientColorTemperatures);
+ final float[] strongDisplayColorTemperatures = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceStrongDisplayColorTemperatures);
final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
displayWhiteBalanceHighLightAmbientBrightnesses,
displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
- ambientColorTemperatures, displayColorTempeartures);
+ ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures,
+ strongDisplayColorTemperatures);
brightnessSensor.setCallbacks(controller);
colorTemperatureSensor.setCallbacks(controller);
return controller;
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
similarity index 64%
rename from services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
rename to services/core/java/com/android/server/pm/InitAppsHelper.java
index 06405ae..2fda02d 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -32,6 +32,7 @@
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Environment;
@@ -61,14 +62,25 @@
* further cleanup and eventually all the installation/scanning related logic will go to another
* class.
*/
-final class InitAndSystemPackageHelper {
+final class InitAppsHelper {
private final PackageManagerService mPm;
-
private final List<ScanPartition> mDirsToScanAsSystem;
private final int mScanFlags;
private final int mSystemParseFlags;
private final int mSystemScanFlags;
private final InstallPackageHelper mInstallPackageHelper;
+ private final ApexManager mApexManager;
+ private final PackageParser2 mPackageParser;
+ private final ExecutorService mExecutorService;
+ /* Tracks how long system scan took */
+ private long mSystemScanTime;
+ /* Track of the number of cached system apps */
+ private int mCachedSystemApps;
+ /* Track of the number of system apps */
+ private int mSystemPackagesCount;
+ private final boolean mIsDeviceUpgrading;
+ private final boolean mIsOnlyCoreApps;
+ private final List<ScanPartition> mSystemPartitions;
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -76,21 +88,34 @@
* are package location.
*/
private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ /* Tracks of any system packages that no longer exist that needs to be pruned. */
+ private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
+ // Tracks of stub packages that must either be replaced with full versions in the /data
+ // partition or be disabled.
+ private final List<String> mStubSystemApps = new ArrayList<>();
// TODO(b/198166813): remove PMS dependency
- InitAndSystemPackageHelper(PackageManagerService pm) {
+ InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
+ InstallPackageHelper installPackageHelper, PackageParser2 packageParser,
+ List<ScanPartition> systemPartitions) {
mPm = pm;
- mInstallPackageHelper = new InstallPackageHelper(pm);
+ mApexManager = apexManager;
+ mInstallPackageHelper = installPackageHelper;
+ mPackageParser = packageParser;
+ mSystemPartitions = systemPartitions;
mDirsToScanAsSystem = getSystemScanPartitions();
+ mIsDeviceUpgrading = mPm.isDeviceUpgrading();
+ mIsOnlyCoreApps = mPm.isOnlyCoreApps();
// Set flag to monitor and not change apk file paths when scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
- if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
+ if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
} else {
mScanFlags = scanFlags;
}
mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ mExecutorService = ParallelPackageParser.makeExecutorService();
}
private List<File> getFrameworkResApkSplitFiles() {
@@ -118,7 +143,7 @@
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
+ scanPartitions.addAll(mSystemPartitions);
scanPartitions.addAll(getApexScanPartitions());
Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
return scanPartitions;
@@ -126,8 +151,7 @@
private List<ScanPartition> getApexScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- final List<ApexManager.ActiveApexInfo> activeApexInfos =
- mPm.mApexManager.getActiveApexInfos();
+ final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
if (scanPartition != null) {
@@ -144,117 +168,133 @@
if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
sp.getFolder().getAbsolutePath())
|| apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
- sp.getFolder().getAbsolutePath() + File.separator)) {
+ sp.getFolder().getAbsolutePath() + File.separator)) {
return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
return null;
}
- public OverlayConfig initPackages(
- WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
- long startTime) {
- PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
-
- ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+ /**
+ * Install apps from system dirs.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public OverlayConfig initSystemApps(WatchedArrayMap<String, PackageSetting> packageSettings,
+ int[] userIds, long startTime) {
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
- mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
+ mApexManager.scanApexPackagesTraced(mPackageParser, mExecutorService);
- scanSystemDirs(packageParser, executorService);
+ scanSystemDirs(mPackageParser, mExecutorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
- for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
- for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
+ for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
- OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+ final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem(),
- apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
- // Prune any system packages that no longer exist.
- final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
- // Stub packages must either be replaced with full versions in the /data
- // partition or be disabled.
- final List<String> stubSystemApps = new ArrayList<>();
+ apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
- if (!mPm.isOnlyCoreApps()) {
+ if (!mIsOnlyCoreApps) {
// do this first before mucking with mPackages for the "expecting better" case
- updateStubSystemAppsList(stubSystemApps);
+ updateStubSystemAppsList(mStubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
- possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+ mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
}
- final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+ logSystemAppsScanningTime(startTime);
+ return overlayConfig;
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void logSystemAppsScanningTime(long startTime) {
+ mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
mPm.mSettings.pruneSharedUsersLPw();
- final long systemScanTime = SystemClock.uptimeMillis() - startTime;
- final int systemPackagesCount = mPm.mPackages.size();
- Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
- + " ms, packageCount: " + systemPackagesCount
+ mSystemScanTime = SystemClock.uptimeMillis() - startTime;
+ mSystemPackagesCount = mPm.mPackages.size();
+ Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
+ + " ms, packageCount: " + mSystemPackagesCount
+ " , timePerPackage: "
- + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
- + " , cached: " + cachedSystemApps);
- if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
+ + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
+ + " , cached: " + mCachedSystemApps);
+ if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
- systemScanTime / systemPackagesCount);
+ mSystemScanTime / mSystemPackagesCount);
//CHECKSTYLE:ON IndentationCheck
}
+ }
- if (!mPm.isOnlyCoreApps()) {
+ /**
+ * Install apps/updates from data dir and fix system apps that are affected.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public void initNonSystemApps(@NonNull int[] userIds, long startTime) {
+ if (!mIsOnlyCoreApps) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
- mScanFlags | SCAN_REQUIRE_KNOWN, 0,
- packageParser, executorService);
-
+ mScanFlags | SCAN_REQUIRE_KNOWN,
+ mPackageParser, mExecutorService);
}
- List<Runnable> unfinishedTasks = executorService.shutdownNow();
+ List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
-
- if (!mPm.isOnlyCoreApps()) {
- mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
- userIds, mScanFlags);
- mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
- stubSystemApps, mSystemScanFlags, mSystemParseFlags);
-
- // Uncompress and install any stubbed system applications.
- // This must be done last to ensure all stubs are replaced or disabled.
- mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
-
- final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- - cachedSystemApps;
-
- final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
- final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
- Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
- + " ms, packageCount: " + dataPackagesCount
- + " , timePerPackage: "
- + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
- + " , cached: " + cachedNonSystemApps);
- if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(
- FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
- dataScanTime / dataPackagesCount);
- //CHECKSTYLE:OFF IndentationCheck
- }
+ if (!mIsOnlyCoreApps) {
+ fixSystemPackages(userIds);
+ logNonSystemAppScanningTime(startTime);
}
mExpectingBetter.clear();
-
mPm.mSettings.pruneRenamedPackagesLPw();
- packageParser.close();
- return overlayConfig;
+ mPackageParser.close();
+ }
+
+ /**
+ * Clean up system packages now that some system package updates have been installed from
+ * the data dir. Also install system stub packages as the last step.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void fixSystemPackages(@NonNull int[] userIds) {
+ mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
+ userIds, mScanFlags);
+ mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+ mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private void logNonSystemAppScanningTime(long startTime) {
+ final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+ - mCachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
+ final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (mIsDeviceUpgrading && dataPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ }
}
/**
@@ -274,14 +314,14 @@
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
- mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
+ mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
List<File> frameworkSplits = getFrameworkResApkSplitFiles();
scanDirTracedLI(frameworkDir, frameworkSplits,
mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
- mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+ mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
@@ -293,11 +333,11 @@
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags,
- mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+ mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
- mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
+ mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
}
@@ -316,11 +356,11 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
final int parseFlags, int scanFlags,
- long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+ PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
- scanFlags, currentTime, packageParser, executorService);
+ scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 50c26f4..83c8472 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -141,7 +141,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -2358,26 +2358,26 @@
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
if (installedPkg.isSystem()) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else {
// If current upgrade specifies particular preference
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// Application explicitly specified internal.
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
} else if (
installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
// App explicitly prefers external. Let policy decide
} else {
// Prefer previous location
if (installedPkg.isExternalStorage()) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL;
}
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL;
}
}
} else {
// Invalid install. Return error code
- return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
+ return InstallLocationUtils.RECOMMEND_FAILED_ALREADY_EXISTS;
}
}
}
@@ -3030,7 +3030,7 @@
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
try {
- return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -3163,7 +3163,7 @@
| ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
final AndroidPackage pkg = scanSystemPackageTracedLI(
- codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+ codePath, parseFlags, scanFlags, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3338,7 +3338,7 @@
mRemovePackageHelper.removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getPath());
- scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
@@ -3359,7 +3359,7 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
- int scanFlags, long currentTime, PackageParser2 packageParser,
+ int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
@@ -3402,7 +3402,7 @@
parseResult.parsedPackage);
}
try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
null);
} catch (PackageManagerException e) {
errorCode = e.error;
@@ -3465,7 +3465,7 @@
try {
final AndroidPackage newPkg = scanSystemPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, 0, null);
+ scanFile, reparseFlags, rescanFlags, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
@@ -3479,14 +3479,14 @@
/**
* Traces a package scan.
- * @see #scanSystemPackageLI(File, int, int, long, UserHandle)
+ * @see #scanSystemPackageLI(File, int, int, UserHandle)
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ int scanFlags, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
- return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+ return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3498,7 +3498,7 @@
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
+ UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
@@ -3514,7 +3514,7 @@
PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
}
- return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, user);
}
/**
@@ -3533,11 +3533,11 @@
@GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @PackageManagerService.ScanFlags int scanFlags,
@Nullable UserHandle user) throws PackageManagerException {
final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
- parsedPackage, parseFlags, scanFlags, currentTime, user);
+ parsedPackage, parseFlags, scanFlags, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
if (scanResult.mSuccess) {
@@ -3715,7 +3715,7 @@
private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @PackageManagerService.ScanFlags int scanFlags,
@Nullable UserHandle user) throws PackageManagerException {
final boolean scanSystemPartition =
(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
@@ -3902,7 +3902,7 @@
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null);
return new Pair<>(scanResult, shouldHideSystemApp);
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index d996fe4..7e845c7 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -35,18 +35,17 @@
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageLite;
-import android.os.Environment;
import android.os.Message;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.storage.StorageManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
import java.util.ArrayList;
@@ -142,12 +141,8 @@
* Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in
* {@link #mInstallFlags}
*/
- private int overrideInstallLocation(PackageInfoLite pkgLite) {
- final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
- }
-
+ private int overrideInstallLocation(String packageName, int recommendedInstallLocation,
+ int installLocation) {
if (mOriginInfo.mStaged) {
// If we're already staged, we've firmly committed to an install location
if (mOriginInfo.mFile != null) {
@@ -155,77 +150,35 @@
} else {
throw new IllegalStateException("Invalid stage location");
}
- } else if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- /*
- * If we are not staged and have too little free space, try to free cache
- * before giving up.
- */
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mPm.mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- mOriginInfo.mResolvedPath, mPackageAbiOverride);
- if (sizeBytes >= 0) {
- synchronized (mPm.mInstallLock) {
- try {
- mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
- mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
- mPackageAbiOverride);
- } catch (Installer.InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
- }
-
- /*
- * The cache free must have deleted the file we downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation =
- PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ if (recommendedInstallLocation < 0) {
+ return InstallLocationUtils.getInstallationErrorCode(recommendedInstallLocation);
+ }
+ // Override with defaults if needed.
+ synchronized (mPm.mLock) {
+ // reader
+ AndroidPackage installedPkg = mPm.mPackages.get(packageName);
+ if (installedPkg != null) {
+ // Currently installed package which the new package is attempting to replace
+ recommendedInstallLocation = InstallLocationUtils.installLocationPolicy(
+ installLocation, recommendedInstallLocation, mInstallFlags,
+ installedPkg.isSystem(), installedPkg.isExternalStorage());
}
}
- int ret = INSTALL_SUCCEEDED;
- int loc = pkgLite.recommendedInstallLocation;
- if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
- ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
- ret = PackageManager.INSTALL_FAILED_INVALID_APK;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- ret = PackageManager.INSTALL_FAILED_INVALID_URI;
- } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
- ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
- } else {
- // Override with defaults if needed.
- loc = mInstallPackageHelper.installLocationPolicy(pkgLite, mInstallFlags);
+ final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
- final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
-
- if (!onInt) {
- // Override install location with flags
- if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- // Set the flag to install on external media.
- mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
- } else {
- // Make sure the flag for installing on external
- // media is unset
- mInstallFlags |= PackageManager.INSTALL_INTERNAL;
- }
+ if (!onInt) {
+ // Override install location with flags
+ if (recommendedInstallLocation == InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL) {
+ // Set the flag to install on external media.
+ mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
+ } else {
+ // Make sure the flag for installing on external media is unset
+ mInstallFlags |= PackageManager.INSTALL_INTERNAL;
}
}
- return ret;
+ return INSTALL_SUCCEEDED;
}
/*
@@ -254,7 +207,21 @@
}
}
- mRet = overrideInstallLocation(pkgLite);
+ final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
+ == InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ // If we are not staged and have too little free space, try to free cache
+ // before giving up.
+ pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
+ pkgLite.recommendedInstallLocation, mPackageLite,
+ mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
+ }
+ mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
+ pkgLite.installLocation);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index ccc375f..8465248 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,7 +84,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ImageUtils;
@@ -782,7 +782,7 @@
// If caller requested explicit location, validity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- if (!PackageHelper.fitsOnInternal(mContext, params)) {
+ if (!InstallLocationUtils.fitsOnInternal(mContext, params)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
@@ -796,7 +796,7 @@
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
- params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
+ params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 390dd3f..7152783 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -143,8 +143,8 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.os.SomeArgs;
import com.android.internal.security.VerityUtils;
@@ -1537,7 +1537,7 @@
if (stageDir != null && lengthBytes > 0) {
mContext.getSystemService(StorageManager.class).allocateBytes(
targetPfd.getFileDescriptor(), lengthBytes,
- PackageHelper.translateAllocateFlags(params.installFlags));
+ InstallLocationUtils.translateAllocateFlags(params.installFlags));
}
if (offsetBytes > 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 79cfa06..9d95ead 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -101,6 +101,7 @@
import android.content.pm.ModuleInfo;
import android.content.pm.PackageChangeEvent;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -129,6 +130,7 @@
import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.content.pm.overlay.OverlayPaths;
+import android.content.pm.parsing.PackageLite;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -189,7 +191,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
@@ -942,7 +944,7 @@
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
private final DeletePackageHelper mDeletePackageHelper;
- private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
+ private final InitAppsHelper mInitAppsHelper;
private final AppDataHelper mAppDataHelper;
private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
@@ -1687,7 +1689,7 @@
mAppDataHelper = testParams.appDataHelper;
mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
- mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
+ mInitAppsHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
mPreferredActivityHelper = testParams.preferredActivityHelper;
mResolveIntentHelper = testParams.resolveIntentHelper;
@@ -1834,7 +1836,8 @@
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
+ mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
+ mInjector.getScanningCachingPackageParser(), mInjector.getSystemPartitions());
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1964,8 +1967,8 @@
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
final int[] userIds = mUserManager.getUserIds();
- mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings,
- userIds, startTime);
+ mOverlayConfig = mInitAppsHelper.initSystemApps(packageSettings, userIds, startTime);
+ mInitAppsHelper.initNonSystemApps(userIds, startTime);
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
@@ -2901,6 +2904,36 @@
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
+ int freeCacheForInstallation(int recommendedInstallLocation, PackageLite pkgLite,
+ String resolvedPath, String mPackageAbiOverride, int installFlags) {
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(resolvedPath,
+ mPackageAbiOverride);
+ if (sizeBytes >= 0) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+ PackageInfoLite pkgInfoLite = PackageManagerServiceUtils.getMinimalPackageInfo(
+ mContext, pkgLite, resolvedPath, installFlags,
+ mPackageAbiOverride);
+ // The cache free must have deleted the file we downloaded to install.
+ if (pkgInfoLite.recommendedInstallLocation
+ == InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI) {
+ pkgInfoLite.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ return pkgInfoLite.recommendedInstallLocation;
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
+ }
+ }
+ return recommendedInstallLocation;
+ }
+
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
@@ -3676,7 +3709,7 @@
// Before everything else, see whether we need to fstrim.
try {
- IStorageManager sm = PackageHelper.getStorageManager();
+ IStorageManager sm = InstallLocationUtils.getStorageManager();
if (sm != null) {
boolean doTrim = false;
final long interval = android.provider.Settings.Global.getLong(
@@ -6595,8 +6628,9 @@
if (getInstallLocation() == loc) {
return true;
}
- if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL
- || loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ if (loc == InstallLocationUtils.APP_INSTALL_AUTO
+ || loc == InstallLocationUtils.APP_INSTALL_INTERNAL
+ || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
android.provider.Settings.Global.putInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
return true;
@@ -6609,7 +6643,7 @@
// allow instant app access
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
+ InstallLocationUtils.APP_INSTALL_AUTO);
}
/** Called by UserManagerService */
@@ -8643,7 +8677,7 @@
}
boolean isExpectingBetter(String packageName) {
- return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
+ return mInitAppsHelper.isExpectingBetter(packageName);
}
int getDefParseFlags() {
@@ -8746,13 +8780,12 @@
}
boolean isOverlayMutable(String packageName) {
- return (mOverlayConfig != null ? mOverlayConfig
- : OverlayConfig.getSystemInstance()).isMutable(packageName);
+ return mOverlayConfig.isMutable(packageName);
}
@ScanFlags int getSystemPackageScanFlags(File codePath) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ mInitAppsHelper.getDirsToScanAsSystem();
@PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
ScanPartition partition = dirsToScanAsSystem.get(i);
@@ -8770,7 +8803,7 @@
Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
int systemScanFlags, int systemParseFlags) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ mInitAppsHelper.getDirsToScanAsSystem();
@ParsingPackageUtils.ParseFlags int reparseFlags = 0;
@PackageManagerService.ScanFlags int rescanFlags = 0;
for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 1caa76d..00ca4ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -108,7 +108,7 @@
public AppDataHelper appDataHelper;
public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
- public InitAndSystemPackageHelper initAndSystemPackageHelper;
+ public InitAppsHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
public PreferredActivityHelper preferredActivityHelper;
public ResolveIntentHelper resolveIntentHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 19c31e0..d6340b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -43,6 +43,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackagePartitions;
import android.content.pm.ResolveInfo;
@@ -79,8 +80,8 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -805,27 +806,37 @@
final PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null || pkg == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
final File packageFile = new File(packagePath);
final long sizeBytes;
try {
- sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
+ sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
} catch (IOException e) {
if (!packageFile.exists()) {
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
} else {
- ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
}
return ret;
}
- final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
- pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);
-
+ final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_INVALID);
+ sessionParams.appPackageName = pkg.getPackageName();
+ sessionParams.installLocation = pkg.getInstallLocation();
+ sessionParams.sizeBytes = sizeBytes;
+ sessionParams.installFlags = flags;
+ final int recommendedInstallLocation;
+ try {
+ recommendedInstallLocation = InstallLocationUtils.resolveInstallLocation(context,
+ sessionParams);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
ret.packageName = pkg.getPackageName();
ret.splitNames = pkg.getSplitNames();
ret.versionCode = pkg.getVersionCode();
@@ -837,7 +848,6 @@
ret.recommendedInstallLocation = recommendedInstallLocation;
ret.multiArch = pkg.isMultiArch();
ret.debuggable = pkg.isDebuggable();
-
return ret;
}
@@ -857,7 +867,7 @@
throw new PackageManagerException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
}
- return PackageHelper.calculateInstalledSize(result.getResult(), abiOverride);
+ return InstallLocationUtils.calculateInstalledSize(result.getResult(), abiOverride);
} catch (PackageManagerException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return -1;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4b59206..d4fcd06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -98,7 +98,7 @@
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -593,7 +593,7 @@
null /* splitApkPaths */, null /* splitRevisionCodes */,
apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */,
null /* splitTypes */);
- sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
+ sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
@@ -1649,11 +1649,11 @@
private int runGetInstallLocation() throws RemoteException {
int loc = mInterface.getInstallLocation();
String locStr = "invalid";
- if (loc == PackageHelper.APP_INSTALL_AUTO) {
+ if (loc == InstallLocationUtils.APP_INSTALL_AUTO) {
locStr = "auto";
- } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
+ } else if (loc == InstallLocationUtils.APP_INSTALL_INTERNAL) {
locStr = "internal";
- } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ } else if (loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
locStr = "external";
}
getOutPrintWriter().println(loc + "[" + locStr + "]");
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 9bfb7d1..9db215e 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -28,7 +28,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.rollback.RollbackInfo;
@@ -43,11 +42,12 @@
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.rollback.RollbackManagerInternal;
import java.io.File;
@@ -291,8 +291,8 @@
throws PackageManagerException {
// Before marking the session as ready, start checkpoint service if available
try {
- if (PackageHelper.getStorageManager().supportsCheckpoint()) {
- PackageHelper.getStorageManager().startCheckpoint(2);
+ if (InstallLocationUtils.getStorageManager().supportsCheckpoint()) {
+ InstallLocationUtils.getStorageManager().startCheckpoint(2);
}
} catch (Exception e) {
// Failed to get hold of StorageManager
@@ -544,7 +544,7 @@
*/
private void checkActiveSessions() throws PackageManagerException {
try {
- checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint());
+ checkActiveSessions(InstallLocationUtils.getStorageManager().supportsCheckpoint());
} catch (RemoteException e) {
throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
"Can't query fs-checkpoint status : " + e);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 29de555..f63f8f4 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -53,7 +53,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -237,7 +237,7 @@
mApexManager.revertActiveSessions();
}
- PackageHelper.getStorageManager().abortChanges(
+ InstallLocationUtils.getStorageManager().abortChanges(
"abort-staged-install", false /*retry*/);
}
} catch (Exception e) {
@@ -674,8 +674,8 @@
boolean needsCheckpoint = false;
boolean supportsCheckpoint = false;
try {
- supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
- needsCheckpoint = PackageHelper.getStorageManager().needsCheckpoint();
+ supportsCheckpoint = InstallLocationUtils.getStorageManager().supportsCheckpoint();
+ needsCheckpoint = InstallLocationUtils.getStorageManager().needsCheckpoint();
} catch (RemoteException e) {
// This means that vold has crashed, and device is in a bad state.
throw new IllegalStateException("Failed to get checkpoint status", e);
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index bb7e55a..1372098 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -32,7 +32,6 @@
import android.content.pm.PackagePartitions;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import android.os.Environment;
import android.os.FileUtils;
import android.os.UserHandle;
@@ -48,6 +47,7 @@
import com.android.internal.policy.AttributeCache;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import java.io.File;
import java.util.ArrayList;
@@ -150,7 +150,7 @@
final AndroidPackage pkg;
try {
pkg = installPackageHelper.scanSystemPackageTracedLI(
- ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
+ ps.getPath(), parseFlags, SCAN_INITIAL, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 73ec2cd..77d6310 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -33,6 +33,7 @@
import android.net.Uri;
import android.os.BatteryStats;
import android.os.Handler;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -215,14 +216,15 @@
* Called when a wake lock is acquired.
*/
public void onWakeLockAcquired(int flags, String tag, String packageName,
- int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
+ int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
+ IWakeLockCallback callback) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
+ "\", packageName=" + packageName
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
-
+ notifyWakeLockListener(callback, true);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -300,8 +302,9 @@
*/
public void onWakeLockChanging(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
- int newFlags, String newTag, String newPackageName, int newOwnerUid,
- int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
+ IWakeLockCallback callback, int newFlags, String newTag, String newPackageName,
+ int newOwnerUid, int newOwnerPid, WorkSource newWorkSource, String newHistoryTag,
+ IWakeLockCallback newCallback) {
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
@@ -323,10 +326,16 @@
} catch (RemoteException ex) {
// Ignore
}
- } else {
- onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag);
+ } else if (!PowerManagerService.isSameCallback(callback, newCallback)) {
+ onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag,
+ null /* Do not notify the old callback */);
onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
- newWorkSource, newHistoryTag);
+ newWorkSource, newHistoryTag, newCallback /* notify the new callback */);
+ } else {
+ onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag,
+ callback);
+ onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
+ newWorkSource, newHistoryTag, newCallback);
}
}
@@ -334,14 +343,15 @@
* Called when a wake lock is released.
*/
public void onWakeLockReleased(int flags, String tag, String packageName,
- int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
+ int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
+ IWakeLockCallback callback) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
+ "\", packageName=" + packageName
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
-
+ notifyWakeLockListener(callback, false);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -859,6 +869,18 @@
return enabled && dndOff;
}
+ private void notifyWakeLockListener(IWakeLockCallback callback, boolean isEnabled) {
+ if (callback != null) {
+ mHandler.post(() -> {
+ try {
+ callback.onStateChanged(isEnabled);
+ } catch (RemoteException e) {
+ throw new IllegalArgumentException("Wakelock.mCallback is already dead.", e);
+ }
+ });
+ }
+ }
+
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index efcfbdd..3857072 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -66,6 +66,7 @@
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelDuration;
@@ -1436,7 +1437,8 @@
}
private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
- String packageName, WorkSource ws, String historyTag, int uid, int pid) {
+ String packageName, WorkSource ws, String historyTag, int uid, int pid,
+ @Nullable IWakeLockCallback callback) {
synchronized (mLock) {
if (displayId != Display.INVALID_DISPLAY) {
final DisplayInfo displayInfo =
@@ -1460,11 +1462,12 @@
boolean notifyAcquire;
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
- if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
+ if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid, callback)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
- uid, pid, ws, historyTag);
- wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
+ uid, pid, ws, historyTag, callback);
+ wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid,
+ callback);
}
notifyAcquire = false;
} else {
@@ -1476,12 +1479,7 @@
}
state.mNumWakeLocks++;
wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag,
- uid, pid, state);
- try {
- lock.linkToDeath(wakeLock, 0);
- } catch (RemoteException ex) {
- throw new IllegalArgumentException("Wake lock is already dead.");
- }
+ uid, pid, state, callback);
mWakeLocks.add(wakeLock);
setWakeLockDisabledStateLocked(wakeLock);
notifyAcquire = true;
@@ -1576,11 +1574,8 @@
mRequestWaitForNegativeProximity = true;
}
- try {
- wakeLock.mLock.unlinkToDeath(wakeLock, 0);
- } catch (NoSuchElementException e) {
- Slog.wtf(TAG, "Failed to unlink wakelock", e);
- }
+ wakeLock.unlinkToDeath();
+ wakeLock.setDisabled(true);
removeWakeLockLocked(wakeLock, index);
}
}
@@ -1650,13 +1645,41 @@
if (!wakeLock.hasSameWorkSource(ws)) {
notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
- ws, historyTag);
+ ws, historyTag, null);
wakeLock.mHistoryTag = historyTag;
wakeLock.updateWorkSource(ws);
}
}
}
+ private void updateWakeLockCallbackInternal(IBinder lock, IWakeLockCallback callback,
+ int callingUid) {
+ synchronized (mLock) {
+ int index = findWakeLockIndexLocked(lock);
+ if (index < 0) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock)
+ + " [not found]");
+ }
+ throw new IllegalArgumentException("Wake lock not active: " + lock
+ + " from uid " + callingUid);
+ }
+
+ WakeLock wakeLock = mWakeLocks.get(index);
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock)
+ + " [" + wakeLock.mTag + "]");
+ }
+
+ if (!isSameCallback(callback, wakeLock.mCallback)) {
+ notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
+ wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
+ wakeLock.mWorkSource, wakeLock.mHistoryTag, callback);
+ wakeLock.mCallback = callback;
+ }
+ }
+ }
+
@GuardedBy("mLock")
private int findWakeLockIndexLocked(IBinder lock) {
final int count = mWakeLocks.size();
@@ -1684,7 +1707,7 @@
wakeLock.mNotifiedAcquired = true;
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
- wakeLock.mHistoryTag);
+ wakeLock.mHistoryTag, wakeLock.mCallback);
restartNofifyLongTimerLocked(wakeLock);
}
}
@@ -1726,11 +1749,13 @@
@GuardedBy("mLock")
private void notifyWakeLockChangingLocked(WakeLock wakeLock, int flags, String tag,
- String packageName, int uid, int pid, WorkSource ws, String historyTag) {
+ String packageName, int uid, int pid, WorkSource ws, String historyTag,
+ IWakeLockCallback callback) {
if (mSystemReady && wakeLock.mNotifiedAcquired) {
mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
- wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag);
+ wakeLock.mHistoryTag, wakeLock.mCallback, flags, tag, packageName, uid, pid, ws,
+ historyTag, callback);
notifyWakeLockLongFinishedLocked(wakeLock);
// Changing the wake lock will count as releasing the old wake lock(s) and
// acquiring the new ones... we do this because otherwise once a wakelock
@@ -1747,7 +1772,7 @@
wakeLock.mAcquireTime = 0;
mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
- wakeLock.mWorkSource, wakeLock.mHistoryTag);
+ wakeLock.mWorkSource, wakeLock.mHistoryTag, wakeLock.mCallback);
notifyWakeLockLongFinishedLocked(wakeLock);
}
}
@@ -4045,10 +4070,7 @@
}
}
}
- if (wakeLock.mDisabled != disabled) {
- wakeLock.mDisabled = disabled;
- return true;
- }
+ return wakeLock.setDisabled(disabled);
}
return false;
}
@@ -5041,10 +5063,11 @@
public boolean mNotifiedAcquired;
public boolean mNotifiedLong;
public boolean mDisabled;
+ public IWakeLockCallback mCallback;
public WakeLock(IBinder lock, int displayId, int flags, String tag, String packageName,
WorkSource workSource, String historyTag, int ownerUid, int ownerPid,
- UidState uidState) {
+ UidState uidState, @Nullable IWakeLockCallback callback) {
mLock = lock;
mDisplayId = displayId;
mFlags = flags;
@@ -5055,15 +5078,43 @@
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
mUidState = uidState;
+ mCallback = callback;
+ linkToDeath();
}
@Override
public void binderDied() {
+ unlinkToDeath();
PowerManagerService.this.handleWakeLockDeath(this);
}
+ private void linkToDeath() {
+ try {
+ mLock.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ throw new IllegalArgumentException("Wakelock.mLock is already dead.");
+ }
+ }
+
+ @GuardedBy("mLock")
+ void unlinkToDeath() {
+ try {
+ mLock.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.wtf(TAG, "Failed to unlink Wakelock.mLock", e);
+ }
+ }
+
+ public boolean setDisabled(boolean disabled) {
+ if (mDisabled != disabled) {
+ mDisabled = disabled;
+ return true;
+ } else {
+ return false;
+ }
+ }
public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
- int ownerUid, int ownerPid) {
+ int ownerUid, int ownerPid, IWakeLockCallback callback) {
return mFlags == flags
&& mTag.equals(tag)
&& hasSameWorkSource(workSource)
@@ -5072,7 +5123,8 @@
}
public void updateProperties(int flags, String tag, String packageName,
- WorkSource workSource, String historyTag, int ownerUid, int ownerPid) {
+ WorkSource workSource, String historyTag, int ownerUid, int ownerPid,
+ IWakeLockCallback callback) {
if (!mPackageName.equals(packageName)) {
throw new IllegalStateException("Existing wake lock package name changed: "
+ mPackageName + " to " + packageName);
@@ -5089,6 +5141,7 @@
mTag = tag;
updateWorkSource(workSource);
mHistoryTag = historyTag;
+ mCallback = callback;
}
public boolean hasSameWorkSource(WorkSource workSource) {
@@ -5307,11 +5360,12 @@
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
- String packageName, int uid, int displayId) {
+ String packageName, int uid, int displayId, IWakeLockCallback callback) {
if (uid < 0) {
uid = Binder.getCallingUid();
}
- acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null, displayId);
+ acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null,
+ displayId, callback);
}
@Override // Binder call
@@ -5346,7 +5400,8 @@
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws, String historyTag, int displayId) {
+ WorkSource ws, String historyTag, int displayId,
+ @Nullable IWakeLockCallback callback) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
@@ -5386,7 +5441,7 @@
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
- uid, pid);
+ uid, pid, callback);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5395,7 +5450,8 @@
@Override // Binder call
public void acquireWakeLockAsync(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag) {
- acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY);
+ acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY,
+ null);
}
@Override // Binder call
@@ -5463,6 +5519,23 @@
}
@Override // Binder call
+ public void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback) {
+ if (lock == null) {
+ throw new IllegalArgumentException("lock must not be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+
+ final int callingUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ updateWakeLockCallbackInternal(lock, callback, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isWakeLockLevelSupported(int level) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -6510,4 +6583,15 @@
}
};
+ static boolean isSameCallback(IWakeLockCallback callback1,
+ IWakeLockCallback callback2) {
+ if (callback1 == callback2) {
+ return true;
+ }
+ if (callback1 != null && callback2 != null
+ && callback1.asBinder() == callback2.asBinder()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerService.java b/services/core/java/com/android/server/resources/ResourcesManagerService.java
new file mode 100644
index 0000000..cc27546
--- /dev/null
+++ b/services/core/java/com/android/server/resources/ResourcesManagerService.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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.resources;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.IResourcesManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import com.android.server.SystemService;
+import com.android.server.am.ActivityManagerService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A service for managing information about ResourcesManagers
+ */
+public class ResourcesManagerService extends SystemService {
+ private ActivityManagerService mActivityManagerService;
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public ResourcesManagerService(@NonNull Context context) {
+ super(context);
+ publishBinderService(Context.RESOURCES_SERVICE, mService);
+ }
+
+ @Override
+ public void onStart() {
+ // Intentionally left empty.
+ }
+
+ private final IBinder mService = new IResourcesManager.Stub() {
+ @Override
+ public boolean dumpResources(String process, ParcelFileDescriptor fd,
+ RemoteCallback callback) throws RemoteException {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+ callback.sendResult(null);
+ throw new SecurityException("dump should only be called by shell");
+ }
+ return mActivityManagerService.dumpResources(process, fd, callback);
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter pw, @Nullable String[] args) {
+ try {
+ mActivityManagerService.dumpAllResources(ParcelFileDescriptor.dup(fd), pw);
+ } catch (Exception e) {
+ pw.println("Exception while trying to dump all resources: " + e.getMessage());
+ e.printStackTrace(pw);
+ }
+ }
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out,
+ @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return (new ResourcesManagerShellCommand(this)).exec(
+ this,
+ in.getFileDescriptor(),
+ out.getFileDescriptor(),
+ err.getFileDescriptor(),
+ args);
+ }
+ };
+
+ public void setActivityManagerService(
+ ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+}
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
new file mode 100644
index 0000000..7d8336a
--- /dev/null
+++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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.resources;
+
+import android.content.res.IResourcesManager;
+import android.os.ConditionVariable;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Shell command handler for resources related commands
+ */
+public class ResourcesManagerShellCommand extends ShellCommand {
+ private static final String TAG = "ResourcesManagerShellCommand";
+
+ private final IResourcesManager mInterface;
+
+ public ResourcesManagerShellCommand(IResourcesManager anInterface) {
+ mInterface = anInterface;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter err = getErrPrintWriter();
+ try {
+ switch (cmd) {
+ case "dump":
+ return dumpResources();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (IllegalArgumentException e) {
+ err.println("Error: " + e.getMessage());
+ } catch (RemoteException e) {
+ err.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int dumpResources() throws RemoteException {
+ String processId = getNextArgRequired();
+ try {
+ ConditionVariable lock = new ConditionVariable();
+ RemoteCallback
+ finishCallback = new RemoteCallback(result -> lock.open(), null);
+
+ if (!mInterface.dumpResources(processId,
+ ParcelFileDescriptor.dup(getOutFileDescriptor()), finishCallback)) {
+ getErrPrintWriter().println("RESOURCES DUMP FAILED on process " + processId);
+ return -1;
+ }
+ lock.block(5000);
+ return 0;
+ } catch (IOException e) {
+ Slog.e(TAG, "Exception while dumping resources", e);
+ getErrPrintWriter().println("Exception while dumping resources: " + e.getMessage());
+ }
+ return -1;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter out = getOutPrintWriter();
+ out.println("Resources manager commands:");
+ out.println(" help");
+ out.println(" Print this help text.");
+ out.println(" dump <PROCESS>");
+ out.println(" Dump the Resources objects in use as well as the history of Resources");
+
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 344edbb..8a87c96 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -158,6 +158,8 @@
private final SparseArray<UiState> mDisplayUiState = new SparseArray<>();
@GuardedBy("mLock")
private IUdfpsHbmListener mUdfpsHbmListener;
+ @GuardedBy("mLock")
+ private IBiometricContextListener mBiometricContextListener;
@GuardedBy("mCurrentRequestAddTilePackages")
private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
@@ -897,6 +899,9 @@
@Override
public void setBiometicContextListener(IBiometricContextListener listener) {
enforceStatusBarService();
+ synchronized (mLock) {
+ mBiometricContextListener = listener;
+ }
if (mBar != null) {
try {
mBar.setBiometicContextListener(listener);
@@ -1327,6 +1332,7 @@
mHandler.post(() -> {
synchronized (mLock) {
setUdfpsHbmListener(mUdfpsHbmListener);
+ setBiometicContextListener(mBiometricContextListener);
}
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3fe6166..5f04b7e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,6 +46,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -241,6 +244,7 @@
import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration;
+import android.app.admin.DevicePolicyManager;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -272,6 +276,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Binder;
@@ -538,6 +543,15 @@
// Tracking splash screen status from previous activity
boolean mSplashScreenStyleEmpty = false;
+ Drawable mEnterpriseThumbnailDrawable;
+
+ private void updateEnterpriseThumbnailDrawable(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ mEnterpriseThumbnailDrawable = dpm.getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> context.getDrawable(R.drawable.ic_corp_badge));
+ }
+
static final int LAUNCH_SOURCE_TYPE_SYSTEM = 1;
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
@@ -1930,6 +1944,8 @@
mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
mActivityRecordInputSink = new ActivityRecordInputSink(this);
+
+ updateEnterpriseThumbnailDrawable(mAtmService.mUiContext);
}
/**
@@ -6993,12 +7009,11 @@
return;
}
final Rect frame = win.getRelativeFrame();
- final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId
- ? R.drawable.ic_account_circle
- : R.drawable.ic_corp_badge;
- final HardwareBuffer thumbnail =
- getDisplayContent().mAppTransition
- .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
+ ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle)
+ : mEnterpriseThumbnailDrawable;
+ final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
+ .createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
if (thumbnail == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d5abe4f..56adcfd 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -93,7 +93,6 @@
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
-import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -101,6 +100,7 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Debug;
@@ -539,8 +539,8 @@
* animation.
*/
HardwareBuffer createCrossProfileAppsThumbnail(
- @DrawableRes int thumbnailDrawableRes, Rect frame) {
- return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ Drawable thumbnailDrawable, Rect frame) {
+ return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 95ef5f7..99abf44 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -103,6 +103,7 @@
"libappfuse",
"libbinder_ndk",
"libbinder",
+ "libchrome",
"libcutils",
"libcrypto",
"liblog",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index df5fb28..8cb27e1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -457,6 +457,9 @@
mInputManager->getReader().dump(dump);
dump += "\n";
+ mInputManager->getUnwantedInteractionBlocker().dump(dump);
+ dump += "\n";
+
mInputManager->getClassifier().dump(dump);
dump += "\n";
@@ -704,6 +707,7 @@
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
ATRACE_CALL();
+ mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
index fbc000b..99d06eb 100644
--- a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
@@ -226,17 +226,18 @@
env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
// Set fields
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
- gnssAntennaInfo.carrierFrequencyMHz);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
- phaseCenterVariationCorrections);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetSignalGainCorrections,
- signalGainCorrections);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
+ gnssAntennaInfo.carrierFrequencyMHz);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset,
+ phaseCenterOffset);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
+ phaseCenterVariationCorrections);
+ callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections,
+ signalGainCorrections);
// build
jobject gnssAntennaInfoObject =
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 6c0d5d9..34ca559 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -212,13 +212,14 @@
jobject gnssMeasurementsEventBuilderObject =
env->NewObject(class_gnssMeasurementsEventBuilder,
method_gnssMeasurementsEventBuilderCtor);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetClock, clock);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray);
- env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
- method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
- gnssAgcArray);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetClock, clock);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetMeasurements,
+ measurementArray);
+ callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject,
+ method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
+ gnssAgcArray);
jobject gnssMeasurementsEventObject =
env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
method_gnssMeasurementsEventBuilderBuild);
@@ -408,24 +409,24 @@
satellitePvt.satClockInfo.satHardwareCodeBiasMeters,
satellitePvt.satClockInfo.satTimeCorrectionMeters,
satellitePvt.satClockInfo.satClkDriftMps);
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetPositionEcef, positionEcef);
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetVelocityEcef, velocityEcef);
- env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetClockInfo,
- clockInfo);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetPositionEcef, positionEcef);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetVelocityEcef, velocityEcef);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetClockInfo, clockInfo);
}
if (satFlags & SatellitePvt::HAS_IONO) {
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetIonoDelayMeters,
- satellitePvt.ionoDelayMeters);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetIonoDelayMeters,
+ satellitePvt.ionoDelayMeters);
}
if (satFlags & SatellitePvt::HAS_TROPO) {
- env->CallObjectMethod(satellitePvtBuilderObject,
- method_satellitePvtBuilderSetTropoDelayMeters,
- satellitePvt.tropoDelayMeters);
+ callObjectMethodIgnoringResult(env, satellitePvtBuilderObject,
+ method_satellitePvtBuilderSetTropoDelayMeters,
+ satellitePvt.tropoDelayMeters);
}
jobject satellitePvtObject =
@@ -453,17 +454,19 @@
jobject correlationVectorBuilderObject =
env->NewObject(class_correlationVectorBuilder,
method_correlationVectorBuilderCtor);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetMagnitude, magnitudeArray);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond,
- correlationVector.frequencyOffsetMps);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetSamplingStartMeters,
- correlationVector.samplingStartM);
- env->CallObjectMethod(correlationVectorBuilderObject,
- method_correlationVectorBuilderSetSamplingWidthMeters,
- correlationVector.samplingWidthM);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetMagnitude,
+ magnitudeArray);
+ callObjectMethodIgnoringResult(
+ env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond,
+ correlationVector.frequencyOffsetMps);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingStartMeters,
+ correlationVector.samplingStartM);
+ callObjectMethodIgnoringResult(env, correlationVectorBuilderObject,
+ method_correlationVectorBuilderSetSamplingWidthMeters,
+ correlationVector.samplingWidthM);
jobject correlationVectorObject =
env->CallObjectMethod(correlationVectorBuilderObject,
method_correlationVectorBuilderBuild);
@@ -519,12 +522,14 @@
const GnssAgc& gnssAgc = agcs[i];
jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
- gnssAgc.agcLevelDb);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType,
- (int)gnssAgc.constellation);
- env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz,
- gnssAgc.carrierFrequencyHz);
+ callObjectMethodIgnoringResult(env, agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
+ gnssAgc.agcLevelDb);
+ callObjectMethodIgnoringResult(env, agcBuilderObject,
+ method_gnssAgcBuilderSetConstellationType,
+ (int)gnssAgc.constellation);
+ callObjectMethodIgnoringResult(env, agcBuilderObject,
+ method_gnssAgcBuilderSetCarrierFrequencyHz,
+ gnssAgc.carrierFrequencyHz);
jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild);
env->SetObjectArrayElement(gnssAgcArray, i, agcObject);
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 40a94ce..8f32c47 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -111,6 +111,13 @@
}
}
+void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...) {
+ va_list args;
+ va_start(args, mid);
+ env->DeleteLocalRef(env->CallObjectMethodV(obj, mid, args));
+ va_end(args);
+}
+
JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor)
: env_(env), clazz_(clazz) {
object_ = env_->NewObject(clazz_, defaultCtor);
diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h
index 2640a77..c8ee661 100644
--- a/services/core/jni/gnss/Utils.h
+++ b/services/core/jni/gnss/Utils.h
@@ -56,6 +56,8 @@
void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...);
+
template <class T>
void logHidlError(hardware::Return<T>& result, const char* errorMessage) {
ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3bda7bf0..ee64a73 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17680,6 +17680,13 @@
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
admin, caller.getUserId(), ACTION_PROVISION_MANAGED_PROFILE);
+ if (nonRequiredApps.isEmpty()) {
+ Slogf.i(LOG_TAG, "No disallowed packages for the managed profile.");
+ } else {
+ for (String packageName : nonRequiredApps) {
+ Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]");
+ }
+ }
userInfo = mUserManager.createProfileForUserEvenWhenDisallowed(
provisioningParams.getProfileName(),
UserManager.USER_TYPE_PROFILE_MANAGED,
@@ -17698,6 +17705,8 @@
startTime,
callerPackage);
+ onCreateAndProvisionManagedProfileStarted(provisioningParams);
+
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
@@ -17718,6 +17727,8 @@
}
}
+ onCreateAndProvisionManagedProfileCompleted(provisioningParams);
+
sendProvisioningCompletedBroadcast(
userInfo.id,
ACTION_PROVISION_MANAGED_PROFILE,
@@ -17739,6 +17750,29 @@
}
}
+ /**
+ * Callback called at the beginning of {@link #createAndProvisionManagedProfile(
+ * ManagedProfileProvisioningParams, String)} after the relevant prechecks have passed.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onCreateAndProvisionManagedProfileStarted(
+ ManagedProfileProvisioningParams provisioningParams) {}
+
+ /**
+ * Callback called at the end of {@link #createAndProvisionManagedProfile(
+ * ManagedProfileProvisioningParams, String)} after all the other provisioning tasks
+ * have completed successfully.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onCreateAndProvisionManagedProfileCompleted(
+ ManagedProfileProvisioningParams provisioningParams) {}
+
private void resetInteractAcrossProfilesAppOps() {
mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps();
pregrantDefaultInteractAcrossProfilesAppOps();
@@ -17978,6 +18012,7 @@
ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
+ onProvisionFullyManagedDeviceStarted(provisioningParams);
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
@@ -18003,6 +18038,7 @@
disallowAddUser();
setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ onProvisionFullyManagedDeviceCompleted(provisioningParams);
sendProvisioningCompletedBroadcast(
deviceOwnerUserId,
ACTION_PROVISION_MANAGED_DEVICE,
@@ -18018,6 +18054,29 @@
}
}
+ /**
+ * Callback called at the beginning of {@link #provisionFullyManagedDevice(
+ * FullyManagedDeviceProvisioningParams, String)} after the relevant prechecks have passed.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onProvisionFullyManagedDeviceStarted(
+ FullyManagedDeviceProvisioningParams provisioningParams) {}
+
+ /**
+ * Callback called at the end of {@link #provisionFullyManagedDevice(
+ * FullyManagedDeviceProvisioningParams, String)} after all the other provisioning tasks
+ * have completed successfully.
+ *
+ * <p>The logic in this method blocks provisioning.
+ *
+ * <p>This method is meant to be overridden by OEMs.
+ */
+ private void onProvisionFullyManagedDeviceCompleted(
+ FullyManagedDeviceProvisioningParams provisioningParams) {}
+
private void setTimeAndTimezone(String timeZone, long localTime) {
try {
final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3fd5cd1..c9aeabd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -176,6 +176,7 @@
import com.android.server.powerstats.PowerStatsService;
import com.android.server.profcollect.ProfcollectForwardingService;
import com.android.server.recoverysystem.RecoverySystemService;
+import com.android.server.resources.ResourcesManagerService;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleServicePlatformHelper;
import com.android.server.rotationresolver.RotationResolverManagerService;
@@ -213,6 +214,8 @@
import dalvik.system.VMRuntime;
+import com.google.android.startop.iorap.IorapForwardingService;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -1283,6 +1286,13 @@
mSystemServiceManager.startService(new OverlayManagerService(mSystemContext));
t.traceEnd();
+ // Manages Resources packages
+ t.traceBegin("StartResourcesManagerService");
+ ResourcesManagerService resourcesService = new ResourcesManagerService(mSystemContext);
+ resourcesService.setActivityManagerService(mActivityManagerService);
+ mSystemServiceManager.startService(resourcesService);
+ t.traceEnd();
+
t.traceBegin("StartSensorPrivacyService");
mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext));
t.traceEnd();
@@ -1615,6 +1625,10 @@
mSystemServiceManager.startService(PinnerService.class);
t.traceEnd();
+ t.traceBegin("IorapForwardingService");
+ mSystemServiceManager.startService(IorapForwardingService.class);
+ t.traceEnd();
+
if (Build.IS_DEBUGGABLE && ProfcollectForwardingService.enabled()) {
t.traceBegin("ProfcollectForwardingService");
mSystemServiceManager.startService(ProfcollectForwardingService.class);
diff --git a/services/startop/Android.bp b/services/startop/Android.bp
new file mode 100644
index 0000000..c56c463
--- /dev/null
+++ b/services/startop/Android.bp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_static {
+ name: "services.startop",
+ defaults: ["platform_service_defaults"],
+
+ static_libs: [
+ // frameworks/base/startop/iorap
+ "services.startop.iorap",
+ ],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index b0ffcf3..317a51b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -304,6 +304,7 @@
throws Exception {
mGameServiceProviderInstance.start();
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -325,6 +326,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation =
@@ -339,6 +341,7 @@
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
@@ -348,6 +351,7 @@
public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -365,7 +369,9 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10,
@@ -379,6 +385,7 @@
public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -394,6 +401,7 @@
public void gameTaskFocused_propagatedToGameSession() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -419,6 +427,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -435,6 +444,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
dispatchTaskRemoved(10);
@@ -452,6 +462,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -469,6 +480,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -489,6 +501,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -516,6 +529,7 @@
startTask(10, GAME_A_MAIN_ACTIVITY);
startTask(11, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -533,6 +547,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -560,6 +575,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -589,6 +605,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -622,10 +639,19 @@
}
@Test
+ public void createGameSession_failurePermissionDenied() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
+ assertThrows(SecurityException.class, () -> mFakeGameService.requestCreateGameSession(10));
+ }
+
+ @Test
public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -653,6 +679,7 @@
public void takeScreenshot_failureNoBitmapCaptured() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
IGameSessionController gameSessionController = getOnlyElement(
@@ -672,6 +699,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
IGameSessionController gameSessionController = getOnlyElement(
@@ -695,6 +723,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -723,6 +752,7 @@
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
FakeGameSession gameSession10 = new FakeGameSession();
@@ -742,10 +772,11 @@
public void restartGame_failurePermissionDenied() throws Exception {
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
+ mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
-
IGameSessionController gameSessionController = Objects.requireNonNull(getOnlyElement(
mFakeGameSessionService.getCapturedCreateInvocations())).mGameSessionController;
+ mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY);
assertThrows(SecurityException.class,
() -> gameSessionController.restartGame(10));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index bdfdf77..64657a9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -50,7 +50,7 @@
import android.util.SparseArray;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
@@ -101,12 +101,12 @@
mMockitoSession = ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
.mockStatic(SystemProperties.class)
- .mockStatic(PackageHelper.class)
+ .mockStatic(InstallLocationUtils.class)
.startMocking();
when(mStorageManager.supportsCheckpoint()).thenReturn(true);
when(mStorageManager.needsCheckpoint()).thenReturn(true);
- when(PackageHelper.getStorageManager()).thenReturn(mStorageManager);
+ when(InstallLocationUtils.getStorageManager()).thenReturn(mStorageManager);
when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true");
when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true");
diff --git a/services/tests/servicestests/src/com/android/server/DockObserverTest.java b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
new file mode 100644
index 0000000..c325778
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 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 static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.R;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockObserverTest {
+
+ @Rule
+ public TestableContext mContext =
+ new TestableContext(ApplicationProvider.getApplicationContext(), null);
+
+ private final BroadcastInterceptingContext mInterceptingContext =
+ new BroadcastInterceptingContext(mContext);
+
+ BroadcastInterceptingContext.FutureIntent updateExtconDockState(DockObserver observer,
+ String extconDockState) {
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ mInterceptingContext.nextBroadcastIntent(Intent.ACTION_DOCK_EVENT);
+ observer.setDockStateFromProviderForTesting(
+ DockObserver.ExtconStateProvider.fromString(extconDockState));
+ TestableLooper.get(this).processAllMessages();
+ return futureIntent;
+ }
+
+ DockObserver observerWithMappingConfig(String[] configEntries) {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.array.config_dockExtconStateMapping,
+ configEntries);
+ return new DockObserver(mInterceptingContext);
+ }
+
+ void assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState,
+ int expectedExtra) throws ExecutionException, InterruptedException {
+ assertThat(updateExtconDockState(observer, extconDockState)
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(expectedExtra);
+ assertThat(updateExtconDockState(observer, "DOCK=0")
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ }
+
+ @Before
+ public void setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ @Test
+ public void testDockIntentBroadcast_onlyAfterBootReady()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = new DockObserver(mInterceptingContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ updateExtconDockState(observer, "DOCK=1");
+ updateExtconDockState(observer, "DOCK=1").assertNotReceived();
+ // Last boot phase reached
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ TestableLooper.get(this).processAllMessages();
+ assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResource()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(
+ new String[] {"2,KEY1=1,KEY2=2", "3,KEY3=3"});
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+
+ // Mapping should not match
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2",
+ Intent.EXTRA_DOCK_STATE_DESK);
+
+ // 1st mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_CAR);
+
+ // 2nd mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY3=3",
+ Intent.EXTRA_DOCK_STATE_LE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResourceWithWildcard()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(new String[] {
+ "2,KEY2=2",
+ "3,KEY3=3",
+ "4"
+ });
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5",
+ Intent.EXTRA_DOCK_STATE_HE_DESK);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index ec884f5..5746f6f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -19,18 +19,25 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.StatusBarManager;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.common.OperationReason;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
import com.google.common.collect.ImmutableList;
@@ -56,6 +63,10 @@
@Mock
private IStatusBarService mStatusBarService;
+ @Mock
+ private ISessionListener mSessionListener;
+ @Mock
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private OperationContext mOpContext = new OperationContext();
private IBiometricContextListener mListener;
@@ -63,11 +74,17 @@
@Before
public void setup() throws RemoteException {
- mProvider = new BiometricContextProvider(mStatusBarService, null /* handler */);
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService,
+ null /* handler */);
ArgumentCaptor<IBiometricContextListener> captor =
ArgumentCaptor.forClass(IBiometricContextListener.class);
verify(mStatusBarService).setBiometicContextListener(captor.capture());
mListener = captor.getValue();
+ ArgumentCaptor<ISessionListener> sessionCaptor =
+ ArgumentCaptor.forClass(ISessionListener.class);
+ verify(mStatusBarService).registerSessionListener(anyInt(), sessionCaptor.capture());
+ mSessionListener = sessionCaptor.getValue();
}
@Test
@@ -76,6 +93,12 @@
assertThat(mProvider.isAoD()).isTrue();
mListener.onDozeChanged(false);
assertThat(mProvider.isAoD()).isFalse();
+
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false);
+ mListener.onDozeChanged(true);
+ assertThat(mProvider.isAoD()).isFalse();
+ mListener.onDozeChanged(false);
+ assertThat(mProvider.isAoD()).isFalse();
}
@Test
@@ -112,4 +135,84 @@
verify(emptyConsumer, never()).accept(any());
verify(nonEmptyConsumer).accept(same(mOpContext));
}
+
+ @Test
+ public void testSessionId() throws RemoteException {
+ final int keyguardSessionId = 10;
+ final int bpSessionId = 20;
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+
+ mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD,
+ InstanceId.fakeInstanceId(keyguardSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+
+ mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ InstanceId.fakeInstanceId(bpSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId);
+
+ mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD,
+ InstanceId.fakeInstanceId(keyguardSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId);
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+
+ mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ InstanceId.fakeInstanceId(bpSessionId));
+
+ assertThat(mProvider.getBiometricPromptSessionId()).isNull();
+ assertThat(mProvider.getKeyguardEntrySessionId()).isNull();
+ }
+
+ @Test
+ public void testUpdate() throws RemoteException {
+ mListener.onDozeChanged(false);
+ OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */);
+
+ // default state when nothing has been set
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(0);
+ assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(mOpContext.isAoD).isEqualTo(false);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+
+ for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ StatusBarManager.SESSION_KEYGUARD)) {
+ final int id = 40 + type;
+ final boolean aod = (type & 1) == 0;
+
+ mListener.onDozeChanged(aod);
+ mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
+ context = mProvider.updateContext(mOpContext, false /* crypto */);
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(id);
+ assertThat(mOpContext.reason).isEqualTo(reason(type));
+ assertThat(mOpContext.isAoD).isEqualTo(aod);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+
+ mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
+ }
+
+ context = mProvider.updateContext(mOpContext, false /* crypto */);
+ assertThat(context).isSameInstanceAs(mOpContext);
+ assertThat(mOpContext.id).isEqualTo(0);
+ assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN);
+ assertThat(mOpContext.isAoD).isEqualTo(false);
+ assertThat(mOpContext.isCrypto).isEqualTo(false);
+ }
+
+ private static byte reason(int type) {
+ if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) {
+ return OperationReason.BIOMETRIC_PROMPT;
+ }
+ if (type == StatusBarManager.SESSION_KEYGUARD) {
+ return OperationReason.KEYGUARD;
+ }
+ return OperationReason.UNKNOWN;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index b0eb810..fe023374 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -31,6 +31,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.input.InputSensorInfo;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -68,10 +69,12 @@
@Mock
private BaseClientMonitor mClient;
+ private OperationContext mOpContext;
private BiometricLogger mLogger;
@Before
public void setUp() {
+ mOpContext = new OperationContext();
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
@@ -93,14 +96,13 @@
final int acquiredInfo = 2;
final int vendorCode = 3;
- final boolean isCrypto = true;
final int targetUserId = 9;
- mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId);
+ mLogger.logOnAcquired(mContext, mOpContext, acquiredInfo, vendorCode, targetUserId);
- verify(mSink).acquired(
+ verify(mSink).acquired(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ eq(acquiredInfo), eq(vendorCode), eq(targetUserId));
}
@Test
@@ -109,17 +111,16 @@
final boolean authenticated = true;
final boolean requireConfirmation = false;
- final boolean isCrypto = false;
final int targetUserId = 11;
final boolean isBiometricPrompt = true;
- mLogger.logOnAuthenticated(mContext,
- authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt);
+ mLogger.logOnAuthenticated(mContext, mOpContext,
+ authenticated, requireConfirmation, targetUserId, isBiometricPrompt);
- verify(mSink).authenticate(
+ verify(mSink).authenticate(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto),
- eq(targetUserId), eq(isBiometricPrompt), anyFloat());
+ anyLong(), anyInt(), eq(requireConfirmation),
+ eq(targetUserId), anyFloat());
}
@Test
@@ -143,14 +144,13 @@
final int error = 7;
final int vendorCode = 11;
- final boolean isCrypto = false;
final int targetUserId = 9;
- mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId);
+ mLogger.logOnError(mContext, mOpContext, error, vendorCode, targetUserId);
- verify(mSink).error(
+ verify(mSink).error(eq(mOpContext),
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
- anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ anyLong(), eq(error), eq(vendorCode), eq(targetUserId));
}
@Test
@@ -175,38 +175,34 @@
private void testDisabledMetrics(boolean isBadConfig) {
mLogger.disableMetrics();
- mLogger.logOnAcquired(mContext,
+ mLogger.logOnAcquired(mContext, mOpContext,
0 /* acquiredInfo */,
1 /* vendorCode */,
- true /* isCrypto */,
8 /* targetUserId */);
- mLogger.logOnAuthenticated(mContext,
+ mLogger.logOnAuthenticated(mContext, mOpContext,
true /* authenticated */,
true /* requireConfirmation */,
- false /* isCrypto */,
4 /* targetUserId */,
true/* isBiometricPrompt */);
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
true /* enrollSuccessful */);
- mLogger.logOnError(mContext,
+ mLogger.logOnError(mContext, mOpContext,
4 /* error */,
0 /* vendorCode */,
- false /* isCrypto */,
6 /* targetUserId */);
- verify(mSink, never()).acquired(
+ verify(mSink, never()).acquired(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyInt(), anyInt(), anyBoolean(), anyInt());
- verify(mSink, never()).authenticate(
+ anyInt(), anyInt(), anyInt());
+ verify(mSink, never()).authenticate(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyLong(), anyBoolean(), anyInt(), anyBoolean(),
- anyBoolean(), anyInt(), anyBoolean(), anyFloat());
+ anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
verify(mSink, never()).enroll(
anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
- verify(mSink, never()).error(
+ verify(mSink, never()).error(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
- anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt());
+ anyLong(), anyInt(), anyInt(), anyInt());
mLogger.logUnknownEnrollmentInFramework();
mLogger.logUnknownEnrollmentInHal();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 25585dd..aba93b0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -16,12 +16,13 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,6 +48,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -57,7 +59,6 @@
private static final int USER_ID = 12;
private static final long OP_ID = 32;
- private static final boolean HAS_AOD = true;
@Rule
public final TestableContext mContext = new TestableContext(
@@ -89,7 +90,8 @@
@Before
public void setup() {
- when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
}
@Test
@@ -106,11 +108,12 @@
final FaceAuthenticationClient client = createClient(2);
client.start(mCallback);
- verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).authenticateWithContext(
+ eq(OP_ID), same(mOperationContextCaptor.getValue()));
verify(mHal, never()).authenticate(anyLong());
-
- final OperationContext opContext = mOperationContextCaptor.getValue();
- assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
}
private FaceAuthenticationClient createClient(int version) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 6c72ebf..25135c6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -16,10 +16,11 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +44,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -52,7 +54,6 @@
public class FaceDetectClientTest {
private static final int USER_ID = 12;
- private static final boolean HAS_AOD = true;
@Rule
public final TestableContext mContext = new TestableContext(
@@ -80,7 +81,8 @@
@Before
public void setup() {
- when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
}
@Test
@@ -97,11 +99,11 @@
final FaceDetectClient client = createClient(2);
client.start(mCallback);
- verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
verify(mHal, never()).detectInteraction();
-
- final OperationContext opContext = mOperationContextCaptor.getValue();
- assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
}
private FaceDetectClient createClient(int version) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 22070e9..38e048b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -17,11 +17,15 @@
package com.android.server.biometrics.sensors.face.aidl;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyByte;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
@@ -38,8 +42,12 @@
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -71,10 +79,18 @@
private ClientMonitorCallback mCallback;
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
+ @Captor
+ private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Before
+ public void setup() {
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
@Test
public void enrollNoContext_v1() throws RemoteException {
final FaceEnrollClient client = createClient(1);
@@ -89,7 +105,11 @@
final FaceEnrollClient client = createClient(2);
client.start(mCallback);
- verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), any());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(),
+ same(mOperationContextCaptor.getValue()));
verify(mHal, never()).enroll(any(), anyByte(), any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 0ac00aa..12b8264 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -37,6 +37,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -60,6 +61,8 @@
private UserManager mUserManager;
@Mock
private IFace mDaemon;
+ @Mock
+ private BiometricContext mBiometricContext;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -89,7 +92,7 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mSensorProps, TAG,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, mBiometricContext);
}
@SuppressWarnings("rawtypes")
@@ -139,8 +142,9 @@
@NonNull Context context,
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- super(context, props, halInstanceName, lockoutResetDispatcher);
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ super(context, props, halInstanceName, lockoutResetDispatcher, biometricContext);
mDaemon = daemon;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 21a7a8a..116d2d5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -41,6 +41,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -70,6 +71,8 @@
private UserManager mUserManager;
@Mock
private BiometricScheduler mScheduler;
+ @Mock
+ private BiometricContext mBiometricContext;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -100,7 +103,8 @@
resetLockoutRequiresChallenge);
Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
- mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
+ mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler,
+ mBiometricContext);
mBinder = new Binder();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 5c360e9..de0f038 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -24,7 +24,9 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -56,6 +58,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -68,7 +71,6 @@
private static final int USER_ID = 8;
private static final long OP_ID = 7;
- private static final boolean HAS_AOD = true;
private static final int POINTER_ID = 0;
private static final int TOUCH_X = 8;
private static final int TOUCH_Y = 20;
@@ -107,6 +109,8 @@
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@Captor
private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+ @Captor
+ private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -115,7 +119,8 @@
public void setup() {
when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
- when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
}
@Test
@@ -132,11 +137,12 @@
final FingerprintAuthenticationClient client = createClient(2);
client.start(mCallback);
- verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).authenticateWithContext(
+ eq(OP_ID), same(mOperationContextCaptor.getValue()));
verify(mHal, never()).authenticate(anyLong());
-
- final OperationContext opContext = mOperationContextCaptor.getValue();
- assertThat(opContext.isAoD).isEqualTo(HAS_AOD);
}
@Test
@@ -205,6 +211,23 @@
}
@Test
+ public void notifyHalWhenContextChanges() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+ client.start(mCallback);
+
+ verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+
+ // fake an update to the context
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ mContextInjector.getValue().accept(opContext);
+ verify(mHal).onContextChanged(eq(opContext));
+
+ client.stopHalOperation();
+ verify(mBiometricContext).unsubscribe(same(opContext));
+ }
+
+ @Test
public void showHideOverlay_cancel() throws RemoteException {
showHideOverlay(c -> c.cancel());
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 295fe47..93cbef1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -16,8 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,6 +45,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -51,7 +55,6 @@
public class FingerprintDetectClientTest {
private static final int USER_ID = 8;
- private static final boolean HAS_AOD = true;
@Rule
public final TestableContext mContext = new TestableContext(
@@ -81,7 +84,8 @@
@Before
public void setup() {
- when(mBiometricContext.isAoD()).thenReturn(HAS_AOD);
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
}
@Test
@@ -100,7 +104,10 @@
client.start(mCallback);
- verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue()));
verify(mHal, never()).detectInteraction();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 7e44eab..5a96f5c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -18,11 +18,15 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,14 +48,18 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -96,14 +104,26 @@
private ClientMonitorCallback mCallback;
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
+ @Mock
+ private Probe mLuxProbe;
@Captor
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@Captor
private ArgumentCaptor<PointerContext> mPointerContextCaptor;
+ @Captor
+ private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Before
+ public void setup() {
+ when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i ->
+ new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
+ when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
+ i -> i.getArgument(0));
+ }
+
@Test
public void enrollNoContext_v1() throws RemoteException {
final FingerprintEnrollClient client = createClient(1);
@@ -120,7 +140,10 @@
client.start(mCallback);
- verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture());
+ InOrder order = inOrder(mHal, mBiometricContext);
+ order.verify(mBiometricContext).updateContext(
+ mOperationContextCaptor.capture(), anyBoolean());
+ order.verify(mHal).enrollWithContext(any(), same(mOperationContextCaptor.getValue()));
verify(mHal, never()).enroll(any());
}
@@ -172,6 +195,41 @@
}
@Test
+ public void luxProbeWhenFingerDown() throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+ client.start(mCallback);
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe).enable();
+
+ client.onAcquired(2, 0);
+ verify(mLuxProbe, never()).disable();
+
+ client.onPointerUp();
+ verify(mLuxProbe).disable();
+
+ client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR);
+ verify(mLuxProbe, times(2)).enable();
+ }
+
+ @Test
+ public void notifyHalWhenContextChanges() throws RemoteException {
+ final FingerprintEnrollClient client = createClient();
+ client.start(mCallback);
+
+ verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture());
+ OperationContext opContext = mOperationContextCaptor.getValue();
+
+ // fake an update to the context
+ verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture());
+ mContextInjector.getValue().accept(opContext);
+ verify(mHal).onContextChanged(eq(opContext));
+
+ client.stopHalOperation();
+ verify(mBiometricContext).unsubscribe(same(opContext));
+ }
+
+ @Test
public void showHideOverlay_cancel() throws RemoteException {
showHideOverlay(c -> c.cancel());
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 73f1516..5a1a02e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -40,6 +40,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -71,6 +72,8 @@
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@Mock
private FingerprintStateCallback mFingerprintStateCallback;
+ @Mock
+ private BiometricContext mBiometricContext;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -105,7 +108,7 @@
mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
mFingerprintStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher);
+ mGestureAvailabilityDispatcher, mBiometricContext);
}
@SuppressWarnings("rawtypes")
@@ -157,9 +160,10 @@
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher,
- gestureAvailabilityDispatcher);
+ gestureAvailabilityDispatcher, biometricContext);
mDaemon = daemon;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index f6b9209..529f994 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
@@ -70,6 +71,8 @@
private BiometricScheduler mScheduler;
@Mock
private FingerprintStateCallback mFingerprintStateCallback;
+ @Mock
+ private BiometricContext mBiometricContext;
private LockoutResetDispatcher mLockoutResetDispatcher;
private Fingerprint21 mFingerprint21;
@@ -101,7 +104,7 @@
mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps,
mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
- mHalResultController);
+ mHalResultController, mBiometricContext);
}
@Test
@@ -126,9 +129,10 @@
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler, @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull HalResultController controller) {
+ @NonNull HalResultController controller,
+ @NonNull BiometricContext biometricContext) {
super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
- lockoutResetDispatcher, controller);
+ lockoutResetDispatcher, controller, biometricContext);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ceb723a..33540c8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -160,11 +160,11 @@
mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), anyInt());
+ nullable(String.class), anyInt(), eq(null));
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
}
@Test
@@ -177,7 +177,7 @@
TestableLooper.get(this).processAllMessages();
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
}
@Test
@@ -196,7 +196,7 @@
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
@@ -212,7 +212,7 @@
verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
- nullable(String.class), eq(displayId));
+ nullable(String.class), eq(displayId), eq(null));
IBinder wakeLock = wakeLockCaptor.getValue();
// Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 0f3742f..ac97911 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -59,6 +59,8 @@
@RunWith(JUnit4.class)
public final class AmbientLuxTest {
+
+ private static final float ALLOWED_ERROR_DELTA = 0.001f;
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
@@ -78,6 +80,8 @@
@Mock private TypedArray mHighLightBiases;
@Mock private TypedArray mAmbientColorTemperatures;
@Mock private TypedArray mDisplayColorTemperatures;
+ @Mock private TypedArray mStrongAmbientColorTemperatures;
+ @Mock private TypedArray mStrongDisplayColorTemperatures;
@Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock;
@Before
@@ -110,6 +114,12 @@
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDisplayColorTemperatures))
.thenReturn(mDisplayColorTemperatures);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceStrongAmbientColorTemperatures))
+ .thenReturn(mStrongAmbientColorTemperatures);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceStrongDisplayColorTemperatures))
+ .thenReturn(mStrongDisplayColorTemperatures);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses))
@@ -375,6 +385,43 @@
}
@Test
+ public void testStrongMode() {
+ final float lowerBrightness = 10.0f;
+ final float upperBrightness = 50.0f;
+ setBrightnesses(lowerBrightness, upperBrightness);
+ setBiases(0.0f, 1.0f);
+ final int ambientColorTempLow = 6000;
+ final int ambientColorTempHigh = 8000;
+ final int displayColorTempLow = 6400;
+ final int displayColorTempHigh = 7400;
+ setStrongAmbientColorTemperatures(ambientColorTempLow, ambientColorTempHigh);
+ setStrongDisplayColorTemperatures(displayColorTempLow, displayColorTempHigh);
+
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ controller.setStrongModeEnabled(true);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
+
+ for (float ambientTempFraction = 0.0f; ambientTempFraction <= 1.0f;
+ ambientTempFraction += 0.1f) {
+ final float ambientTemp =
+ (ambientColorTempHigh - ambientColorTempLow) * ambientTempFraction
+ + ambientColorTempLow;
+ setEstimatedColorTemperature(controller, ambientTemp);
+ for (float brightnessFraction = 0.0f; brightnessFraction <= 1.0f;
+ brightnessFraction += 0.1f) {
+ setEstimatedBrightnessAndUpdate(controller,
+ mix(lowerBrightness, upperBrightness, brightnessFraction));
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE,
+ mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction),
+ brightnessFraction),
+ ALLOWED_ERROR_DELTA);
+ }
+ }
+ }
+
+ @Test
public void testLowLight_DefaultAmbient() throws Exception {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
@@ -486,6 +533,14 @@
setFloatArrayResource(mDisplayColorTemperatures, vals);
}
+ private void setStrongAmbientColorTemperatures(float... vals) {
+ setFloatArrayResource(mStrongAmbientColorTemperatures, vals);
+ }
+
+ private void setStrongDisplayColorTemperatures(float... vals) {
+ setFloatArrayResource(mStrongDisplayColorTemperatures, vals);
+ }
+
private void setFloatArrayResource(TypedArray array, float[] vals) {
when(array.length()).thenReturn(vals.length);
for (int i = 0; i < vals.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
index b621a44..869ac88 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java
@@ -70,7 +70,7 @@
import androidx.test.filters.Suppress;
import com.android.frameworks.servicestests.R;
-import com.android.internal.content.PackageHelper;
+import com.android.internal.content.InstallLocationUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
@@ -106,11 +106,11 @@
private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
- private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO;
+ private static final int APP_INSTALL_AUTO = InstallLocationUtils.APP_INSTALL_AUTO;
- private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL;
+ private static final int APP_INSTALL_DEVICE = InstallLocationUtils.APP_INSTALL_INTERNAL;
- private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL;
+ private static final int APP_INSTALL_SDCARD = InstallLocationUtils.APP_INSTALL_EXTERNAL;
void failStr(String errMsg) {
Log.w(TAG, "errMsg=" + errMsg);
@@ -1214,7 +1214,7 @@
int origDefaultLoc = getDefaultInstallLoc();
InstallParams ip = null;
try {
- setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+ setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO);
// Install first
ip = installFromRawResource("install.apk", rawResId, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
@@ -1303,7 +1303,7 @@
InstallParams ip = null;
try {
PackageManager pm = getPm();
- setInstallLoc(PackageHelper.APP_INSTALL_AUTO);
+ setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO);
// Install first
ip = installFromRawResource("install.apk", R.raw.install, installFlags, false,
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
@@ -1517,11 +1517,11 @@
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
boolean enable = getUserSettingSetInstallLocation();
if (enable) {
- if (userSetting == PackageHelper.APP_INSTALL_AUTO) {
+ if (userSetting == InstallLocationUtils.APP_INSTALL_AUTO) {
iloc = PackageInfo.INSTALL_LOCATION_AUTO;
- } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) {
+ } else if (userSetting == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL;
- } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) {
+ } else if (userSetting == InstallLocationUtils.APP_INSTALL_INTERNAL) {
iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
}
}
@@ -1552,7 +1552,7 @@
}
@LargeTest
public void testExistingIUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@@ -1564,14 +1564,14 @@
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@LargeTest
public void testExistingIUserA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iFlags = PackageManager.INSTALL_INTERNAL;
setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
}
@@ -1616,7 +1616,7 @@
}
@LargeTest
public void testUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@@ -1628,14 +1628,14 @@
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@LargeTest
public void testUserA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iloc = getExpectedInstallLocation(userSetting);
setUserX(true, userSetting, iloc);
}
@@ -1646,7 +1646,7 @@
*/
@LargeTest
public void testUserPrefOffUserI() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
@@ -1658,14 +1658,14 @@
return;
}
- int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
+ int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
@LargeTest
public void testUserPrefOffA() throws Exception {
- int userSetting = PackageHelper.APP_INSTALL_AUTO;
+ int userSetting = InstallLocationUtils.APP_INSTALL_AUTO;
int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
setUserX(false, userSetting, iloc);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 51dbd97..827349a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
@@ -102,6 +103,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -479,21 +481,21 @@
// First, ensure that a normal full wake lock does not cause a wakeup
int flags = PowerManager.FULL_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Ensure that the flag does *NOT* work with a partial wake lock.
flags = PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Verify that flag forces a wakeup when paired to a FULL_WAKE_LOCK
flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
}
@@ -661,12 +663,12 @@
wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]);
return null;
}).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(),
- anyInt(), any(), any());
+ anyInt(), any(), any(), any());
doAnswer(inv -> {
wakelockMap.remove((String) inv.getArguments()[1]);
return null;
}).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(),
- anyInt(), any(), any());
+ anyInt(), any(), any(), any());
//
// TEST STARTS HERE
@@ -679,7 +681,7 @@
// Create a wakelock
mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active.
// Confirm that the wakelocks have been disabled when the forceSuspend is in flight.
@@ -737,7 +739,7 @@
// Take a nap and verify we no longer hold the blocker
int flags = PowerManager.DOZE_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
@@ -893,7 +895,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
advanceTime(60);
@@ -919,7 +921,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null);
advanceTime(1500);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
@@ -995,7 +997,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
@@ -1035,7 +1037,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
@@ -1076,7 +1078,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */, nonDefaultDisplay);
+ null /* workSource */, null /* historyTag */, nonDefaultDisplay, null);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
WAKEFULNESS_AWAKE);
@@ -1640,7 +1642,65 @@
IBinder token = new Binder();
String packageName = "pkg.name";
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
+ null /* callback */);
return mService.findWakeLockLocked(token);
}
+
+ /**
+ * Test IPowerManager.acquireWakeLock() with a IWakeLockCallback.
+ */
+ @Test
+ public void testNotifyWakeLockCallback() {
+ createService();
+ startSystem();
+ final String tag = "wakelock1";
+ final String packageName = "pkg.name";
+ final IBinder token = new Binder();
+ final int flags = PowerManager.PARTIAL_WAKE_LOCK;
+ final IWakeLockCallback callback = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder = Mockito.mock(Binder.class);
+ when(callback.asBinder()).thenReturn(callbackBinder);
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback);
+ verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback));
+
+ mService.getBinderServiceInstance().releaseWakeLock(token, 0);
+ verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback));
+ }
+
+ /**
+ * Test IPowerManager.updateWakeLockCallback() with a new IWakeLockCallback.
+ */
+ @Test
+ public void testNotifyWakeLockCallbackChange() {
+ createService();
+ startSystem();
+ final String tag = "wakelock1";
+ final String packageName = "pkg.name";
+ final IBinder token = new Binder();
+ int flags = PowerManager.PARTIAL_WAKE_LOCK;
+ final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder1 = Mockito.mock(Binder.class);
+ when(callback1.asBinder()).thenReturn(callbackBinder1);
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback1);
+ verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback1));
+
+ final IWakeLockCallback callback2 = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder2 = Mockito.mock(Binder.class);
+ when(callback2.asBinder()).thenReturn(callbackBinder2);
+ mService.getBinderServiceInstance().updateWakeLockCallback(token, callback2);
+ verify(mNotifierMock).onWakeLockChanging(anyInt(), eq(tag), eq(packageName),
+ anyInt(), anyInt(), any(), any(), same(callback1),
+ anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), any(), any(),
+ same(callback2));
+
+ mService.getBinderServiceInstance().releaseWakeLock(token, 0);
+ verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(),
+ anyInt(), any(), any(), same(callback2));
+ }
}
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
new file mode 100644
index 0000000..4fdf34c
--- /dev/null
+++ b/startop/iorap/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2018 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.startop.iorap-javasources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "services.startop.iorap-sources",
+ srcs: [
+ ":services.startop.iorap-javasources",
+ ":iorap-aidl",
+ ],
+ visibility: ["//frameworks/base/services:__subpackages__"],
+}
+
+java_library_static {
+ name: "services.startop.iorap",
+ srcs: [":services.startop.iorap-sources"],
+ libs: ["services.core"],
+}
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/TEST_MAPPING
new file mode 100644
index 0000000..8c9d4df
--- /dev/null
+++ b/startop/iorap/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "libiorap-java-tests"
+ }
+ ],
+ "imports": [
+ {
+ "path": "system/iorap"
+ }
+ ]
+}
diff --git a/startop/iorap/functional_tests/Android.bp b/startop/iorap/functional_tests/Android.bp
new file mode 100644
index 0000000..43c6155
--- /dev/null
+++ b/startop/iorap/functional_tests/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "iorap-functional-tests",
+ srcs: ["src/**/*.java"],
+ data: [":iorap-functional-test-apps"],
+ static_libs: [
+ // Non-test dependencies
+ // library under test
+ "services.startop.iorap",
+ // Test Dependencies
+ // test android dependencies
+ "platform-test-annotations",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator_uiautomator",
+ // test framework dependencies
+ "truth-prebuilt",
+ ],
+ dxflags: ["--multi-dex"],
+ test_suites: ["device-tests"],
+ compile_multilib: "both",
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ certificate: "platform",
+ platform_apis: true,
+}
diff --git a/startop/iorap/functional_tests/AndroidManifest.xml b/startop/iorap/functional_tests/AndroidManifest.xml
new file mode 100644
index 0000000..6bddc4a3
--- /dev/null
+++ b/startop/iorap/functional_tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.startop.iorap.tests"
+ android:sharedUserId="com.google.android.startop.iorap.tests.functional"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <!--suppress AndroidDomInspection -->
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.google.android.startop.iorap.tests" />
+
+ <!--
+ 'debuggable=true' is required to properly load mockito jvmti dependencies,
+ otherwise it gives the following error at runtime:
+
+ Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+ Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml
new file mode 100644
index 0000000..31d4f6c
--- /dev/null
+++ b/startop/iorap/functional_tests/AndroidTest.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<configuration description="Runs iorap-functional-tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="iorap-functional-tests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer
+ class="com.android.tradefed.targetprep.DeviceSetup">
+
+ <!-- iorapd does not pick up the above changes until we restart it -->
+ <option name="run-command" value="stop iorapd" />
+
+ <!-- Clean up the existing iorap database. -->
+ <option name="run-command" value="rm -r /data/misc/iorapd/*" />
+ <option name="run-command" value="sleep 1" />
+
+ <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. -->
+ <option name="run-command" value="setprop iorapd.perfetto.enable true" />
+ <option name="run-command" value="setprop iorapd.readahead.enable true" />
+ <option name="run-command" value="setprop iorapd.log.verbose true" />
+
+ <option name="run-command" value="start iorapd" />
+
+ <!-- give it some time to restart the service; otherwise the first unit test might fail -->
+ <option name="run-command" value="sleep 1" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="abort-on-push-failure" value="true" />
+ <option name="push-file"
+ key="iorap_test_app_v1.apk"
+ value="/data/misc/iorapd/iorap_test_app_v1.apk" />
+ <option name="push-file"
+ key="iorap_test_app_v2.apk"
+ value="/data/misc/iorapd/iorap_test_app_v2.apk" />
+ <option name="push-file"
+ key="iorap_test_app_v3.apk"
+ value="/data/misc/iorapd/iorap_test_app_v3.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.google.android.startop.iorap.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <!-- test-timeout unit is ms, value = 30 min -->
+ <option name="test-timeout" value="1800000" />
+ </test>
+
+</configuration>
+
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
new file mode 100644
index 0000000..5352be6
--- /dev/null
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2020 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.google.android.startop.iorapd;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.Date;
+import java.util.function.BooleanSupplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.List;
+import java.text.SimpleDateFormat;
+
+/**
+ * Test for the work flow of iorap.
+ *
+ * <p> This test tests the function of iorap from:
+ * perfetto collection -> compilation -> prefetching -> version update -> perfetto collection.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IorapWorkFlowTest {
+ private static final String TAG = "IorapWorkFlowTest";
+
+ private static final String TEST_APP_VERSION_ONE_PATH = "/data/misc/iorapd/iorap_test_app_v1.apk";
+ private static final String TEST_APP_VERSION_TWO_PATH = "/data/misc/iorapd/iorap_test_app_v2.apk";
+ private static final String TEST_APP_VERSION_THREE_PATH = "/data/misc/iorapd/iorap_test_app_v3.apk";
+
+ private static final String DB_PATH = "/data/misc/iorapd/sqlite.db";
+ private static final Duration TIMEOUT = Duration.ofSeconds(300L);
+
+ private UiDevice mDevice;
+
+ @Before
+ public void setUp() throws Exception {
+ // Initialize UiDevice instance
+ mDevice = UiDevice.getInstance(getInstrumentation());
+
+ // Start from the home screen
+ mDevice.pressHome();
+
+ // Wait for launcher
+ final String launcherPackage = mDevice.getLauncherPackageName();
+ assertThat(launcherPackage, notNullValue());
+ mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ String packageName = "com.example.ioraptestapp";
+ uninstallApk(packageName);
+ }
+
+ @Test (timeout = 300000)
+ public void testNormalWorkFlow() throws Exception {
+ assertThat(mDevice, notNullValue());
+
+ // Install test app version one
+ installApk(TEST_APP_VERSION_ONE_PATH);
+ String packageName = "com.example.ioraptestapp";
+ String activityName = "com.example.ioraptestapp.MainActivity";
+
+ // Perfetto trace collection phase.
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/1L));
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/1L));
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/1L));
+
+ // Trigger maintenance service for compilation.
+ TimeUnit.SECONDS.sleep(5L);
+ assertTrue(compile(packageName, activityName, /*version=*/1L));
+
+ // Run app with prefetching
+ assertTrue(startAppWithCompiledTrace(
+ packageName, activityName, /*version=*/1L));
+ }
+
+ @Test (timeout = 300000)
+ public void testUpdateApp() throws Exception {
+ assertThat(mDevice, notNullValue());
+
+ // Install test app version two,
+ String packageName = "com.example.ioraptestapp";
+ String activityName = "com.example.ioraptestapp.MainActivity";
+ installApk(TEST_APP_VERSION_TWO_PATH);
+
+ // Perfetto trace collection phase.
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/2L));
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/2L));
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/2L));
+
+ // Trigger maintenance service for compilation.
+ TimeUnit.SECONDS.sleep(5L);
+ assertTrue(compile(packageName, activityName, /*version=*/2L));
+
+ // Run app with prefetching
+ assertTrue(startAppWithCompiledTrace(
+ packageName, activityName, /*version=*/2L));
+
+ // Update test app to version 3
+ installApk(TEST_APP_VERSION_THREE_PATH);
+
+ // Rerun app, should do pefetto tracing.
+ assertTrue(startAppForPerfettoTrace(
+ packageName, activityName, /*version=*/3L));
+ }
+
+ private static void installApk(String apkPath) throws Exception {
+ // Disable the selinux to allow pm install apk in the dir.
+ executeShellCommand("setenforce 0");
+ executeShellCommand("pm install -r -d " + apkPath);
+ executeShellCommand("setenforce 1");
+
+ }
+
+ private static void uninstallApk(String apkPath) throws Exception {
+ executeShellCommand("pm uninstall " + apkPath);
+ }
+
+ /**
+ * Starts the testing app to collect the perfetto trace.
+ *
+ * @param expectPerfettoTraceCount is the expected count of perfetto traces.
+ */
+ private boolean startAppForPerfettoTrace(
+ String packageName, String activityName, long version)
+ throws Exception {
+ LogcatTimestamp timestamp = runAppOnce(packageName, activityName);
+ return waitForPerfettoTraceSavedFromLogcat(
+ packageName, activityName, version, timestamp);
+ }
+
+ private boolean startAppWithCompiledTrace(
+ String packageName, String activityName, long version)
+ throws Exception {
+ LogcatTimestamp timestamp = runAppOnce(packageName, activityName);
+ return waitForPrefetchingFromLogcat(
+ packageName, activityName, version, timestamp);
+ }
+
+ private LogcatTimestamp runAppOnce(String packageName, String activityName) throws Exception {
+ // Close the specified app if it's open
+ closeApp(packageName);
+ LogcatTimestamp timestamp = new LogcatTimestamp();
+ // Launch the specified app
+ startApp(packageName, activityName);
+ // Wait for the app to appear
+ mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), TIMEOUT.getSeconds());
+ return timestamp;
+ }
+
+ // Invokes the maintenance to compile the perfetto traces to compiled trace.
+ private boolean compile(
+ String packageName, String activityName, long version) throws Exception {
+ // The job id (283673059) is defined in class IorapForwardingService.
+ executeShellCommandViaTmpFile("cmd jobscheduler run -f android 283673059");
+ return waitForFileExistence(getCompiledTracePath(packageName, activityName, version));
+ }
+
+ private String getCompiledTracePath(
+ String packageName, String activityName, long version) {
+ return String.format(
+ "/data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb",
+ packageName, version, activityName);
+ }
+
+ /**
+ * Starts the testing app.
+ */
+ private void startApp(String packageName, String activityName) throws Exception {
+ executeShellCommandViaTmpFile(
+ String.format("am start %s/%s", packageName, activityName));
+ }
+
+ /**
+ * Closes the testing app.
+ * <p> Keep trying to kill the process of the app until no process of the app package
+ * appears.</p>
+ */
+ private void closeApp(String packageName) throws Exception {
+ while (true) {
+ String pid = executeShellCommand("pidof " + packageName);
+ if (pid.isEmpty()) {
+ Log.i(TAG, "Closed app " + packageName);
+ return;
+ }
+ executeShellCommand("kill -9 " + pid);
+ TimeUnit.SECONDS.sleep(1L);
+ }
+ }
+
+ /** Waits for a file to appear. */
+ private boolean waitForFileExistence(String fileName) throws Exception {
+ return retryWithTimeout(TIMEOUT, () -> {
+ try {
+ String fileExists = executeShellCommandViaTmpFile(
+ String.format("test -f %s; echo $?", fileName));
+ Log.i(TAG, fileName + " existence is " + fileExists);
+ return fileExists.trim().equals("0");
+ } catch (Exception e) {
+ Log.i(TAG, e.getMessage());
+ return false;
+ }
+ });
+ }
+
+ /** Waits for the perfetto trace saved message from logcat. */
+ private boolean waitForPerfettoTraceSavedFromLogcat(
+ String packageName, String activityName, long version, LogcatTimestamp timestamp)
+ throws Exception {
+ Pattern p = Pattern.compile(".*"
+ + getPerfettoTraceSavedIndicator(packageName, activityName, version)
+ + "(.*[.]perfetto_trace[.]pb)\n.*", Pattern.DOTALL);
+
+ return retryWithTimeout(TIMEOUT, () -> {
+ try {
+ String log = timestamp.getLogcatAfter();
+ Matcher m = p.matcher(log);
+ Log.d(TAG, "Tries to find perfetto trace...");
+ if (!m.matches()) {
+ Log.i(TAG, "Cannot find perfetto trace saved in log.");
+ return false;
+ }
+ String filePath = m.group(1);
+ Log.i(TAG, "Perfetto trace is saved to " + filePath);
+ return true;
+ } catch(Exception e) {
+ Log.e(TAG, e.getMessage());
+ return false;
+ }
+ });
+ }
+
+ private String getPerfettoTraceSavedIndicator(
+ String packageName, String activityName, long version) {
+ return String.format(
+ "Perfetto TraceBuffer saved to file: /data/misc/iorapd/%s/%d/%s/raw_traces/",
+ packageName, version, activityName);
+ }
+
+ /**
+ * Waits for the prefetching log in the logcat.
+ *
+ * <p> When prefetching works, the perfetto traces should not be collected. </p>
+ */
+ private boolean waitForPrefetchingFromLogcat(
+ String packageName, String activityName, long version, LogcatTimestamp timestamp)
+ throws Exception {
+ Pattern p = Pattern.compile(
+ ".*" + getReadaheadIndicator(packageName, activityName, version) +
+ ".*Total File Paths=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
+ + ".*Total Entries=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
+ + ".*Total Bytes=(\\d+) \\(good: (\\d+[.]?\\d*)%\\).*",
+ Pattern.DOTALL);
+
+ return retryWithTimeout(TIMEOUT, () -> {
+ try {
+ String log = timestamp.getLogcatAfter();
+ Matcher m = p.matcher(log);
+ if (!m.matches()) {
+ Log.i(TAG, "Cannot find readahead log.");
+ return false;
+ }
+
+ int totalFilePath = Integer.parseInt(m.group(1));
+ float totalFilePathGoodRate = Float.parseFloat(m.group(2)) / 100;
+ int totalEntries = Integer.parseInt(m.group(3));
+ float totalEntriesGoodRate = Float.parseFloat(m.group(4)) / 100;
+ int totalBytes = Integer.parseInt(m.group(5));
+ float totalBytesGoodRate = Float.parseFloat(m.group(6)) / 100;
+
+ Log.i(TAG, String.format(
+ "totalFilePath: %d (good %.2f) totalEntries: %d (good %.2f) totalBytes: %d (good %.2f)",
+ totalFilePath, totalFilePathGoodRate, totalEntries, totalEntriesGoodRate, totalBytes,
+ totalBytesGoodRate));
+
+ return totalFilePath > 0 &&
+ totalEntries > 0 &&
+ totalBytes > 0 &&
+ totalFilePathGoodRate > 0.5 &&
+ totalEntriesGoodRate > 0.5 &&
+ totalBytesGoodRate > 0.5;
+ } catch(Exception e) {
+ return false;
+ }
+ });
+ }
+
+ private static String getReadaheadIndicator(
+ String packageName, String activityName, long version) {
+ return String.format(
+ "Description = /data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb",
+ packageName, version, activityName);
+ }
+
+ /** Retry until timeout. */
+ private boolean retryWithTimeout(Duration timeout, BooleanSupplier supplier) throws Exception {
+ long totalSleepTimeSeconds = 0L;
+ long sleepIntervalSeconds = 2L;
+ while (true) {
+ if (supplier.getAsBoolean()) {
+ return true;
+ }
+ TimeUnit.SECONDS.sleep(sleepIntervalSeconds);
+ totalSleepTimeSeconds += sleepIntervalSeconds;
+ if (totalSleepTimeSeconds > timeout.getSeconds()) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Executes command in adb shell via a tmp file.
+ *
+ * <p> This should be run as root.</p>
+ */
+ private static String executeShellCommandViaTmpFile(String cmd) throws Exception {
+ Log.i(TAG, "Execute via tmp file: " + cmd);
+ Path tmp = null;
+ try {
+ tmp = Files.createTempFile(/*prefix=*/null, /*suffix=*/".sh");
+ Files.write(tmp, cmd.getBytes(StandardCharsets.UTF_8));
+ tmp.toFile().setExecutable(true);
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).
+ executeShellCommand(tmp.toString());
+ } finally {
+ if (tmp != null) {
+ Files.delete(tmp);
+ }
+ }
+ }
+
+ /**
+ * Executes command in adb shell.
+ *
+ * <p> This should be run as root.</p>
+ */
+ private static String executeShellCommand(String cmd) throws Exception {
+ Log.i(TAG, "Execute: " + cmd);
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ }
+
+ static class LogcatTimestamp {
+ private String epochTime;
+
+ public LogcatTimestamp() throws Exception{
+ long currentTimeMillis = System.currentTimeMillis();
+ epochTime = String.format(
+ "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000);
+ Log.i(TAG, "Current logcat timestamp is " + epochTime);
+ }
+
+ // For example, 1585264100.000
+ public String getEpochTime() {
+ return epochTime;
+ }
+
+ // Gets the logcat after this epoch time.
+ public String getLogcatAfter() throws Exception {
+ return executeShellCommandViaTmpFile(
+ "logcat -v epoch -t '" + epochTime + "'");
+ }
+ }
+}
+
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
new file mode 100644
index 0000000..1d38f4c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an activity has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br />
+ *
+ * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type.
+ * All other states could be terminal, see below: <br /><br />
+ *
+ * <pre>
+ *
+ * ┌──────────────────────────────────────┐
+ * │ ▼
+ * ┌─────────┐ ╔════════════════╗ ╔═══════════╗
+ * ──▶ │ STARTED │ ──▶ ║ COMPLETED ║ ──▶ ║ CANCELLED ║
+ * └─────────┘ ╚════════════════╝ ╚═══════════╝
+ * │
+ * │
+ * ▼
+ * ╔════════════════╗
+ * ║ POST_COMPLETED ║
+ * ╚════════════════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot -->
+ *
+ * @hide
+ */
+public class ActivityHintEvent implements Parcelable {
+
+ public static final int TYPE_STARTED = 0;
+ public static final int TYPE_CANCELLED = 1;
+ public static final int TYPE_COMPLETED = 2;
+ public static final int TYPE_POST_COMPLETED = 3;
+ private static final int TYPE_MAX = TYPE_POST_COMPLETED;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_STARTED,
+ TYPE_CANCELLED,
+ TYPE_COMPLETED,
+ TYPE_POST_COMPLETED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final ActivityInfo activityInfo;
+
+ public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) {
+ this.type = type;
+ this.activityInfo = activityInfo;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(activityInfo, "activityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, activityInfo: %s}", type, activityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityHintEvent) {
+ return equals((ActivityHintEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityHintEvent other) {
+ return type == other.type &&
+ Objects.equals(activityInfo, other.activityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ activityInfo.writeToParcel(out, flags);
+ }
+
+ private ActivityHintEvent(Parcel in) {
+ this.type = in.readInt();
+ this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityHintEvent> CREATOR
+ = new Parcelable.Creator<ActivityHintEvent>() {
+ public ActivityHintEvent createFromParcel(Parcel in) {
+ return new ActivityHintEvent(in);
+ }
+
+ public ActivityHintEvent[] newArray(int size) {
+ return new ActivityHintEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
new file mode 100644
index 0000000..f47a42c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import java.util.Objects;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Provide minimal information for launched activities to iorap.<br /><br />
+ *
+ * This uniquely identifies a system-wide activity by providing the {@link #packageName} and
+ * {@link #activityName}.
+ *
+ * @see ActivityHintEvent
+ * @see AppIntentEvent
+ *
+ * @hide
+ */
+public class ActivityInfo implements Parcelable {
+
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+ /** The name of the activity, for example {@code .activities.activity.MainActivity} */
+ public final String activityName;
+
+ public ActivityInfo(String packageName, String activityName) {
+ this.packageName = packageName;
+ this.activityName = activityName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(activityName, "activityName");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageName: %s, activityName: %s}", packageName, activityName);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof ActivityInfo) {
+ return equals((ActivityInfo) other);
+ }
+ return false;
+ }
+
+ private boolean equals(ActivityInfo other) {
+ return Objects.equals(packageName, other.packageName) &&
+ Objects.equals(activityName, other.activityName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(packageName);
+ out.writeString(activityName);
+ }
+
+ private ActivityInfo(Parcel in) {
+ packageName = in.readString();
+ activityName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ActivityInfo> CREATOR
+ = new Parcelable.Creator<ActivityInfo>() {
+ public ActivityInfo createFromParcel(Parcel in) {
+ return new ActivityInfo(in);
+ }
+
+ public ActivityInfo[] newArray(int size) {
+ return new ActivityInfo[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
new file mode 100644
index 0000000..1cd37b5
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br />
+ *
+ * Intent defaults provide a mechanism for an app to register itself as an automatic handler.
+ * For example the camera app might be registered as the default handler for
+ * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently,
+ * if an arbitrary other app requests for a still image camera photo to be taken, the system
+ * will launch the respective default camera app to be launched to handle that request.<br /><br />
+ *
+ * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of
+ * applications that resolve from the default intent. If the application would now be resolved
+ * differently, iorapd would unpin the old application and pin the new application.<br /><br />
+ *
+ * @hide
+ */
+public class AppIntentEvent implements Parcelable {
+
+ /** @see android.content.Intent#CATEGORY_DEFAULT */
+ public static final int TYPE_DEFAULT_INTENT_CHANGED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_DEFAULT_INTENT_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ public final ActivityInfo oldActivityInfo;
+ public final ActivityInfo newActivityInfo;
+
+ // TODO: Probably need the corresponding action here as well.
+
+ public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo,
+ newActivityInfo);
+ }
+
+ private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo,
+ ActivityInfo newActivityInfo) {
+ this.type = type;
+ this.oldActivityInfo = oldActivityInfo;
+ this.newActivityInfo = newActivityInfo;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(oldActivityInfo, "oldActivityInfo");
+ Objects.requireNonNull(oldActivityInfo, "newActivityInfo");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo,
+ newActivityInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof AppIntentEvent) {
+ return equals((AppIntentEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(AppIntentEvent other) {
+ return type == other.type &&
+ Objects.equals(oldActivityInfo, other.oldActivityInfo) &&
+ Objects.equals(newActivityInfo, other.newActivityInfo);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ oldActivityInfo.writeToParcel(out, flags);
+ newActivityInfo.writeToParcel(out, flags);
+ }
+
+ private AppIntentEvent(Parcel in) {
+ this.type = in.readInt();
+ this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+ this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<AppIntentEvent> CREATOR
+ = new Parcelable.Creator<AppIntentEvent>() {
+ public AppIntentEvent createFromParcel(Parcel in) {
+ return new AppIntentEvent(in);
+ }
+
+ public AppIntentEvent[] newArray(int size) {
+ return new AppIntentEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
new file mode 100644
index 0000000..8263e0a
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br />
+ * @see com.android.server.wm.ActivityMetricsLaunchObserver
+ * ActivityMetricsLaunchObserver for the possible event states.
+ * @hide
+ */
+public abstract class AppLaunchEvent implements Parcelable {
+ @LongDef
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SequenceId {}
+
+ public final @SequenceId
+ long sequenceId;
+
+ protected AppLaunchEvent(@SequenceId long sequenceId) {
+ this.sequenceId = sequenceId;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof AppLaunchEvent) {
+ return equals((AppLaunchEvent) other);
+ }
+ return false;
+ }
+
+ protected boolean equals(AppLaunchEvent other) {
+ return sequenceId == other.sequenceId;
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() +
+ "{" + "sequenceId=" + Long.toString(sequenceId) +
+ toStringBody() + "}";
+ }
+
+ protected String toStringBody() { return ""; };
+
+ // List of possible variants:
+
+ public static final class IntentStarted extends AppLaunchEvent {
+ @NonNull
+ public final Intent intent;
+ public final long timestampNs;
+
+ public IntentStarted(@SequenceId long sequenceId,
+ Intent intent,
+ long timestampNs) {
+ super(sequenceId);
+ this.intent = intent;
+ this.timestampNs = timestampNs;
+
+ Objects.requireNonNull(intent, "intent");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof IntentStarted) {
+ return intent.equals(((IntentStarted)other).intent) &&
+ timestampNs == ((IntentStarted)other).timestampNs &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", intent=" + intent.toString() +
+ " , timestampNs=" + Long.toString(timestampNs);
+ }
+
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ IntentProtoParcelable.write(p, intent, flags);
+ p.writeLong(timestampNs);
+ }
+
+ IntentStarted(Parcel p) {
+ super(p);
+ intent = IntentProtoParcelable.create(p);
+ timestampNs = p.readLong();
+ }
+ }
+
+ public static final class IntentFailed extends AppLaunchEvent {
+ public IntentFailed(@SequenceId long sequenceId) {
+ super(sequenceId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof IntentFailed) {
+ return super.equals(other);
+ }
+ return false;
+ }
+
+ IntentFailed(Parcel p) {
+ super(p);
+ }
+ }
+
+ public static abstract class BaseWithActivityRecordData extends AppLaunchEvent {
+ public final @NonNull
+ @ActivityRecordProto byte[] activityRecordSnapshot;
+
+ protected BaseWithActivityRecordData(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot) {
+ super(sequenceId);
+ activityRecordSnapshot = snapshot;
+
+ Objects.requireNonNull(snapshot, "snapshot");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof BaseWithActivityRecordData) {
+ return (Arrays.equals(activityRecordSnapshot,
+ ((BaseWithActivityRecordData)other).activityRecordSnapshot)) &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", " + new String(activityRecordSnapshot);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+ }
+
+ BaseWithActivityRecordData(Parcel p) {
+ super(p);
+ activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+ }
+ }
+
+ public static final class ActivityLaunched extends BaseWithActivityRecordData {
+ public final @ActivityMetricsLaunchObserver.Temperature
+ int temperature;
+
+ public ActivityLaunched(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ @ActivityMetricsLaunchObserver.Temperature int temperature) {
+ super(sequenceId, snapshot);
+ this.temperature = temperature;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunched) {
+ return temperature == ((ActivityLaunched)other).temperature &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", temperature=" + Integer.toString(temperature);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeInt(temperature);
+ }
+
+ ActivityLaunched(Parcel p) {
+ super(p);
+ temperature = p.readInt();
+ }
+ }
+
+ public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
+ public ActivityLaunchFinished(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
+ super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunchFinished) {
+ return timestampNs == ((ActivityLaunchFinished)other).timestampNs &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ActivityLaunchFinished(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
+ }
+
+ public static class ActivityLaunchCancelled extends AppLaunchEvent {
+ public final @Nullable @ActivityRecordProto byte[] activityRecordSnapshot;
+
+ public ActivityLaunchCancelled(@SequenceId long sequenceId,
+ @Nullable @ActivityRecordProto byte[] snapshot) {
+ super(sequenceId);
+ activityRecordSnapshot = snapshot;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunchCancelled) {
+ return Arrays.equals(activityRecordSnapshot,
+ ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", " + new String(activityRecordSnapshot);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ if (activityRecordSnapshot != null) {
+ p.writeBoolean(true);
+ ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+ } else {
+ p.writeBoolean(false);
+ }
+ }
+
+ ActivityLaunchCancelled(Parcel p) {
+ super(p);
+ if (p.readBoolean()) {
+ activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+ } else {
+ activityRecordSnapshot = null;
+ }
+ }
+ }
+
+ public static final class ReportFullyDrawn extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
+ public ReportFullyDrawn(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
+ super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ReportFullyDrawn) {
+ return timestampNs == ((ReportFullyDrawn)other).timestampNs &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ReportFullyDrawn(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
+ }
+
+ @Override
+ public @ContentsFlags int describeContents() { return 0; }
+
+ @Override
+ public void writeToParcel(Parcel p, @WriteFlags int flags) {
+ p.writeInt(getTypeIndex());
+
+ writeToParcelImpl(p, flags);
+ }
+
+
+ public static Creator<AppLaunchEvent> CREATOR =
+ new Creator<AppLaunchEvent>() {
+ @Override
+ public AppLaunchEvent createFromParcel(Parcel source) {
+ int typeIndex = source.readInt();
+
+ Class<?> kls = getClassFromTypeIndex(typeIndex);
+ if (kls == null) {
+ throw new IllegalArgumentException("Invalid type index: " + typeIndex);
+ }
+
+ try {
+ return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source);
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ throw new AssertionError(e);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public AppLaunchEvent[] newArray(int size) {
+ return new AppLaunchEvent[0];
+ }
+ };
+
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ p.writeLong(sequenceId);
+ }
+
+ protected AppLaunchEvent(Parcel p) {
+ sequenceId = p.readLong();
+ }
+
+ private int getTypeIndex() {
+ for (int i = 0; i < sTypes.length; ++i) {
+ if (sTypes[i].equals(this.getClass())) {
+ return i;
+ }
+ }
+ throw new AssertionError("sTypes did not include this type: " + this.getClass());
+ }
+
+ private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) {
+ if (typeIndex >= 0 && typeIndex < sTypes.length) {
+ return sTypes[typeIndex];
+ }
+ return null;
+ }
+
+ // Index position matters: It is used to encode the specific type in parceling.
+ // Keep up-to-date with C++ side.
+ private static Class<?>[] sTypes = new Class[] {
+ IntentStarted.class,
+ IntentFailed.class,
+ ActivityLaunched.class,
+ ActivityLaunchFinished.class,
+ ActivityLaunchCancelled.class,
+ ReportFullyDrawn.class,
+ };
+
+ public static class ActivityRecordProtoParcelable {
+ public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot,
+ int flags) {
+ p.writeByteArray(activityRecordSnapshot);
+ }
+
+ public static @ActivityRecordProto byte[] create(Parcel p) {
+ byte[] data = p.createByteArray();
+
+ return data;
+ }
+ }
+
+ public static class IntentProtoParcelable {
+ private static final int INTENT_PROTO_CHUNK_SIZE = 1024;
+
+ public static void write(Parcel p, @NonNull Intent intent, int flags) {
+ // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
+ // so create a new one every time.
+ final ProtoOutputStream protoOutputStream =
+ new ProtoOutputStream(INTENT_PROTO_CHUNK_SIZE);
+ // Write this data out as the top-most IntentProto (i.e. it is not a sub-object).
+ intent.dumpDebug(protoOutputStream);
+ final byte[] bytes = protoOutputStream.getBytes();
+
+ p.writeByteArray(bytes);
+ }
+
+ // TODO: Should be mockable for testing?
+ // We cannot deserialize in the platform because we don't have a 'readFromProto'
+ // code.
+ public static @NonNull Intent create(Parcel p) {
+ // This will "read" the correct amount of data, but then we discard it.
+ byte[] data = p.createByteArray();
+
+ // Never called by real code in a platform, this binder API is implemented only in C++.
+ return new Intent("<cannot deserialize IntentProto>");
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
new file mode 100644
index 0000000..34aedd7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+/**
+ * Convenience short-hand to throw {@link IllegalAccessException} when the arguments
+ * are out-of-range.
+ */
+public class CheckHelpers {
+ /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */
+ public static void checkTypeInRange(int type, int maxValue) {
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ String.format("type must be non-negative (value=%d)", type));
+ }
+ if (type > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("type out of range (value=%d, max=%d)", type, maxValue));
+ }
+ }
+
+ /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */
+ public static void checkStateInRange(int state, int maxValue) {
+ if (state < 0) {
+ throw new IllegalArgumentException(
+ String.format("state must be non-negative (value=%d)", state));
+ }
+ if (state > maxValue) {
+ throw new IllegalArgumentException(
+ String.format("state out of range (value=%d, max=%d)", state, maxValue));
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java b/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java
new file mode 100644
index 0000000..72c5eaa
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a package is updated by dexopt service.<br /><br />
+ *
+ * @hide
+ */
+public class DexOptEvent implements Parcelable {
+ public static final int TYPE_PACKAGE_UPDATE = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_PACKAGE_UPDATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final String packageName;
+
+ @NonNull
+ public static DexOptEvent createPackageUpdate(String packageName) {
+ return new DexOptEvent(TYPE_PACKAGE_UPDATE, packageName);
+ }
+
+ private DexOptEvent(@Type int type, String packageName) {
+ this.type = type;
+ this.packageName = packageName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(packageName, "packageName");
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{DexOptEvent: packageName: %s}", packageName);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof DexOptEvent) {
+ return equals((DexOptEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(DexOptEvent other) {
+ return packageName.equals(other.packageName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ out.writeString(packageName);
+ }
+
+ private DexOptEvent(Parcel in) {
+ this.type = in.readInt();
+ this.packageName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<DexOptEvent> CREATOR
+ = new Parcelable.Creator<DexOptEvent>() {
+ public DexOptEvent createFromParcel(Parcel in) {
+ return new DexOptEvent(in);
+ }
+
+ public DexOptEvent[] newArray(int size) {
+ return new DexOptEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
new file mode 100644
index 0000000..dcaff26
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -0,0 +1,267 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
+/**
+ * A validator to check the correctness of event sequence during app startup.
+ *
+ * <p> A valid state transition of event sequence is shown as the following:
+ *
+ * <pre>
+ *
+ * +--------------------+
+ * | |
+ * | INIT |
+ * | |
+ * +--------------------+
+ * |
+ * |
+ * ↓
+ * +--------------------+
+ * | |
+ * +-------------------| INTENT_STARTED | ←--------------------------------+
+ * | | | |
+ * | +--------------------+ |
+ * | | |
+ * | | |
+ * ↓ ↓ |
+ * +--------------------+ +--------------------+ |
+ * | | | | |
+ * | INTENT_FAILED | | ACTIVITY_LAUNCHED |------------------+ |
+ * | | | | | |
+ * +--------------------+ +--------------------+ | |
+ * | | | |
+ * | ↓ ↓ |
+ * | +--------------------+ +--------------------+ |
+ * | | | | | |
+ * +------------------ | ACTIVITY_FINISHED | | ACTIVITY_CANCELLED | |
+ * | | | | | |
+ * | +--------------------+ +--------------------+ |
+ * | | | |
+ * | | | |
+ * | ↓ | |
+ * | +--------------------+ | |
+ * | | | | |
+ * | | REPORT_FULLY_DRAWN | | |
+ * | | | | |
+ * | +--------------------+ | |
+ * | | | |
+ * | | | |
+ * | ↓ | |
+ * | +--------------------+ | |
+ * | | | | |
+ * +-----------------→ | END |←-----------------+ |
+ * | | |
+ * +--------------------+ |
+ * | |
+ * | |
+ * | |
+ * +---------------------------------------------
+ *
+ * <p> END is not a real state in implementation. All states that points to END directly
+ * could transition to INTENT_STARTED.
+ *
+ * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state
+ * could be accumulated, because during the UNKNOWN state more IntentStarted may
+ * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted
+ * should termniate.
+ *
+ * <p> During UNKNOWN state, each IntentStarted increases the accumulation, and any of
+ * IntentFailed, ActivityLaunchCancelled and ActivityFinished decreases the accumulation.
+ * ReportFullyDrawn doesn't impact the accumulation.
+ */
+public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
+ static final String TAG = "EventSequenceValidator";
+ /** $> adb shell 'setprop log.tag.EventSequenceValidator VERBOSE' */
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private State state = State.INIT;
+ private long accIntentStartedEvents = 0;
+
+ @Override
+ public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("IntentStarted during UNKNOWN. " + intent);
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ if (state != State.INIT &&
+ state != State.INTENT_FAILED &&
+ state != State.ACTIVITY_CANCELLED &&
+ state != State.ACTIVITY_FINISHED &&
+ state != State.REPORT_FULLY_DRAWN) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
+ incAccIntentStartedEvents();
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
+ state = State.INTENT_STARTED;
+ }
+
+ @Override
+ public void onIntentFailed() {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("onIntentFailed during UNKNOWN.");
+ decAccIntentStartedEvents();
+ return;
+ }
+ if (state != State.INTENT_STARTED) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
+ state = State.INTENT_FAILED;
+ }
+
+ @Override
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("onActivityLaunched during UNKNOWN.");
+ return;
+ }
+ if (state != State.INTENT_STARTED) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
+ state = State.ACTIVITY_LAUNCHED;
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("onActivityLaunchCancelled during UNKNOWN.");
+ decAccIntentStartedEvents();
+ return;
+ }
+ if (state != State.ACTIVITY_LAUNCHED) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED));
+ state = State.ACTIVITY_CANCELLED;
+ }
+
+ @Override
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("onActivityLaunchFinished during UNKNOWN.");
+ decAccIntentStartedEvents();
+ return;
+ }
+
+ if (state != State.ACTIVITY_LAUNCHED) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
+ incAccIntentStartedEvents();
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED));
+ state = State.ACTIVITY_FINISHED;
+ }
+
+ @Override
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (state == State.UNKNOWN) {
+ logWarningWithStackTrace("onReportFullyDrawn during UNKNOWN.");
+ return;
+ }
+ if (state == State.INIT) {
+ return;
+ }
+
+ if (state != State.ACTIVITY_FINISHED) {
+ logWarningWithStackTrace(
+ String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
+ return;
+ }
+
+ Log.i(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
+ state = State.REPORT_FULLY_DRAWN;
+ }
+
+ enum State {
+ INIT,
+ INTENT_STARTED,
+ INTENT_FAILED,
+ ACTIVITY_LAUNCHED,
+ ACTIVITY_CANCELLED,
+ ACTIVITY_FINISHED,
+ REPORT_FULLY_DRAWN,
+ UNKNOWN,
+ }
+
+ private void incAccIntentStartedEvents() {
+ if (accIntentStartedEvents < 0) {
+ throw new AssertionError("The number of unknowns cannot be negative");
+ }
+ if (accIntentStartedEvents == 0) {
+ state = State.UNKNOWN;
+ }
+ ++accIntentStartedEvents;
+ Log.i(TAG,
+ String.format("inc AccIntentStartedEvents to %d", accIntentStartedEvents));
+ }
+
+ private void decAccIntentStartedEvents() {
+ if (accIntentStartedEvents <= 0) {
+ throw new AssertionError("The number of unknowns cannot be negative");
+ }
+ if(accIntentStartedEvents == 1) {
+ state = State.INIT;
+ }
+ --accIntentStartedEvents;
+ Log.i(TAG,
+ String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
+ }
+
+ private void logWarningWithStackTrace(String log) {
+ if (DEBUG) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+ Log.wtf(TAG, String.format("%s\n%s", log, sw));
+ }
+ }
+}
+
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
new file mode 100644
index 0000000..77046f2
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+// TODO: rename to com.android.server.startop.iorap
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.pm.BackgroundDexOptService;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
+
+/**
+ * System-server-local proxy into the {@code IIorap} native service.
+ */
+public class IorapForwardingService extends SystemService {
+
+ public static final String TAG = "IorapForwardingService";
+ /** $> adb shell 'setprop log.tag.IorapForwardingService VERBOSE' */
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ /** $> adb shell 'setprop ro.iorapd.enable true' */
+ private static boolean IS_ENABLED = SystemProperties.getBoolean("ro.iorapd.enable", true);
+ /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */
+ private static boolean WTF_CRASH = SystemProperties.getBoolean(
+ "iorapd.forwarding_service.wtf_crash", false);
+ private static final Duration TIMEOUT = Duration.ofSeconds(600L);
+
+ // "Unique" job ID from the service name. Also equal to 283673059.
+ public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd");
+ // Run every 24 hours.
+ public static final long JOB_INTERVAL_MS = TimeUnit.HOURS.toMillis(24);
+
+ private IIorap mIorapRemote;
+ private final Object mLock = new Object();
+ /** Handle onBinderDeath by periodically trying to reconnect. */
+ private final Handler mHandler =
+ new BinderConnectionHandler(IoThread.getHandler().getLooper());
+
+ private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever).
+ private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null).
+
+
+ /**
+ * Atomics set to true if the JobScheduler requests an abort.
+ */
+ private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false);
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public IorapForwardingService(Context context) {
+ super(context);
+
+ if (DEBUG) {
+ Log.v(TAG, "IorapForwardingService (Context=" + context.toString() + ")");
+ }
+
+ if (sSelfService != null) {
+ throw new AssertionError("only one service instance allowed");
+ }
+ sSelfService = this;
+ }
+
+ //<editor-fold desc="Providers">
+ /*
+ * Providers for external dependencies:
+ * - These are marked as protected to allow tests to inject different values via mocks.
+ */
+
+ @VisibleForTesting
+ protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() {
+ ActivityTaskManagerInternal amtInternal =
+ LocalServices.getService(ActivityTaskManagerInternal.class);
+ ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+ amtInternal.getLaunchObserverRegistry();
+ return launchObserverRegistry;
+ }
+
+ @VisibleForTesting
+ protected IIorap provideIorapRemote() {
+ IIorap iorap;
+ try {
+ iorap = IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ Log.w(TAG, e.getMessage());
+ return null;
+ }
+
+ try {
+ iorap.asBinder().linkToDeath(provideDeathRecipient(), /*flags*/0);
+ } catch (RemoteException e) {
+ handleRemoteError(e);
+ return null;
+ }
+
+ return iorap;
+ }
+
+ @VisibleForTesting
+ protected DeathRecipient provideDeathRecipient() {
+ return new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.w(TAG, "iorapd has died");
+ retryConnectToRemoteAndConfigure(/*attempts*/0);
+
+ if (mJobService != null) {
+ mJobService.onIorapdDisconnected();
+ }
+ }
+ };
+ }
+
+ @VisibleForTesting
+ protected boolean isIorapEnabled() {
+ // These two mendel flags should match those in iorapd native process
+ // system/iorapd/src/common/property.h
+ boolean isTracingEnabled =
+ getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false);
+ boolean isReadAheadEnabled =
+ getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false);
+ // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process
+ // never comes up, so all binder connections will fail indefinitely.
+ return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled);
+ }
+
+ private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) {
+ // TODO(yawanng) use DeviceConfig to get mendel property.
+ // DeviceConfig doesn't work and the reason is not clear.
+ // Provider service is already up before IORapForwardService.
+ String mendelProperty = "persist.device_config."
+ + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
+ + "."
+ + mendelFlag;
+ return SystemProperties.getBoolean(mendelProperty,
+ SystemProperties.getBoolean(sysProperty, defaultValue));
+ }
+
+ //</editor-fold>
+
+ @Override
+ public void onStart() {
+ if (DEBUG) {
+ Log.v(TAG, "onStart");
+ }
+
+ retryConnectToRemoteAndConfigure(/*attempts*/0);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ if (DEBUG) {
+ Log.v(TAG, "onBootPhase(PHASE_BOOT_COMPLETED)");
+ }
+
+ if (isIorapEnabled()) {
+ // Set up a recurring background job. This has to be done in a later phase since it
+ // has a dependency the job scheduler.
+ //
+ // Doing this too early can result in a ServiceNotFoundException for 'jobservice'
+ // or a null reference for #getSystemService(JobScheduler.class)
+ mJobService = new IorapdJobService(getContext());
+ }
+ }
+ }
+
+ private class BinderConnectionHandler extends Handler {
+ public BinderConnectionHandler(android.os.Looper looper) {
+ super(looper);
+ }
+
+ public static final int MESSAGE_BINDER_CONNECT = 0;
+
+ private int mAttempts = 0;
+
+ @Override
+ public void handleMessage(android.os.Message message) {
+ switch (message.what) {
+ case MESSAGE_BINDER_CONNECT:
+ if (!retryConnectToRemoteAndConfigure(mAttempts)) {
+ mAttempts++;
+ } else {
+ mAttempts = 0;
+ }
+ break;
+ default:
+ throw new AssertionError("Unknown message: " + message.toString());
+ }
+ }
+ }
+
+ /**
+ * Handle iorapd shutdowns and crashes, by attempting to reconnect
+ * until the service is reached again.
+ *
+ * <p>The first connection attempt is synchronous,
+ * subsequent attempts are done by posting delayed tasks to the IoThread.</p>
+ *
+ * @return true if connection succeeded now, or false if it failed now [and needs to requeue].
+ */
+ private boolean retryConnectToRemoteAndConfigure(int attempts) {
+ final int sleepTime = 1000; // ms
+
+ if (DEBUG) {
+ Log.v(TAG, "retryConnectToRemoteAndConfigure - attempt #" + attempts);
+ }
+
+ if (connectToRemoteAndConfigure()) {
+ return true;
+ }
+
+ // Either 'iorapd' is stuck in a crash loop (ouch!!) or we manually
+ // called 'adb shell stop iorapd' , which means this would loop until it comes back
+ // up.
+ //
+ // TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid
+ // printing this warning.
+ if (DEBUG) {
+ Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime);
+ }
+
+ // Use a handler instead of Thread#sleep to avoid backing up the binder thread
+ // when this is called from the death recipient callback.
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(BinderConnectionHandler.MESSAGE_BINDER_CONNECT),
+ sleepTime);
+
+ return false;
+
+ // Log.e(TAG, "Can't connect to iorapd - giving up after " + attempts + " attempts");
+ }
+
+ private boolean connectToRemoteAndConfigure() {
+ synchronized (mLock) {
+ // Synchronize against any concurrent calls to this via the DeathRecipient.
+ return connectToRemoteAndConfigureLocked();
+ }
+ }
+
+ private boolean connectToRemoteAndConfigureLocked() {
+ if (!isIorapEnabled()) {
+ if (DEBUG) {
+ Log.v(TAG, "connectToRemoteAndConfigure - iorapd is disabled, skip rest of work");
+ }
+ // When we see that iorapd is disabled (when system server comes up),
+ // it stays disabled permanently until the next system server reset.
+
+ // TODO: consider listening to property changes as a callback, then we can
+ // be more dynamic about handling enable/disable.
+ return true;
+ }
+
+ // Connect to the native binder service.
+ mIorapRemote = provideIorapRemote();
+ if (mIorapRemote == null) {
+ if (DEBUG) {
+ Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
+ }
+ return false;
+ }
+ invokeRemote(mIorapRemote,
+ (IIorap remote) -> remote.setTaskListener(new RemoteTaskListener()) );
+ registerInProcessListenersLocked();
+
+ Log.i(TAG, "Connected to iorapd native service.");
+
+ return true;
+ }
+
+ private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
+ private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator();
+ private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated();
+ private boolean mRegisteredListeners = false;
+
+ private void registerInProcessListenersLocked() {
+ if (mRegisteredListeners) {
+ // Listeners are registered only once (idempotent operation).
+ //
+ // Today listeners are tolerant of the remote side going away
+ // by handling remote errors.
+ //
+ // We could try to 'unregister' the listener when we get a binder disconnect,
+ // but we'd still have to handle the case of encountering synchronous errors so
+ // it really wouldn't be a win (other than having less log spew).
+ return;
+ }
+
+ // Listen to App Launch Sequence events from ActivityTaskManager,
+ // and forward them to the native binder service.
+ ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+ provideLaunchObserverRegistry();
+ launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
+ launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
+
+ BackgroundDexOptService.getService().addPackagesUpdatedListener(
+ mDexOptPackagesUpdated);
+
+
+ mRegisteredListeners = true;
+ }
+
+ private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
+ @Override
+ public void onPackagesUpdated(ArraySet<String> updatedPackages) {
+ String[] updated = updatedPackages.toArray(new String[0]);
+ for (String packageName : updated) {
+ Log.d(TAG, "onPackagesUpdated: " + packageName);
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onDexOptEvent(RequestId.nextValueForSequence(),
+ DexOptEvent.createPackageUpdate(packageName))
+ );
+ }
+ }
+ }
+
+ private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+ // We add a synthetic sequence ID here to make it easier to differentiate new
+ // launch sequences on the native side.
+ private @AppLaunchEvent.SequenceId long mSequenceId = -1;
+
+ // All callbacks occur on the same background thread. Don't synchronize explicitly.
+
+ @Override
+ public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
+ // #onIntentStarted [is the only transition that] initiates a new launch sequence.
+ ++mSequenceId;
+
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)",
+ mSequenceId, intent, timestampNs));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs))
+ );
+ }
+
+ @Override
+ public void onIntentFailed() {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.IntentFailed(mSequenceId))
+ );
+ }
+
+ @Override
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)",
+ mSequenceId, activity, temperature));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
+ );
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)",
+ mSequenceId, activity));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
+ activity)));
+ }
+
+ @Override
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunchFinished(mSequenceId,
+ activity,
+ timestampNs))
+ );
+ }
+
+ @Override
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs))
+ );
+ }
+ }
+
+ /**
+ * Debugging:
+ *
+ * $> adb shell dumpsys jobscheduler
+ *
+ * Search for 'IorapdJobServiceProxy'.
+ *
+ * JOB #1000/283673059: 6e54ed android/com.google.android.startop.iorap.IorapForwardingService$IorapdJobServiceProxy
+ * ^ ^ ^
+ * (uid, job id) ComponentName(package/class)
+ *
+ * Forcing the job to be run, ignoring constraints:
+ *
+ * $> adb shell cmd jobscheduler run -f android 283673059
+ * ^ ^
+ * package job_id
+ *
+ * ------------------------------------------------------------
+ *
+ * This class is instantiated newly by the JobService every time
+ * it wants to run a new job.
+ *
+ * We need to forward invocations to the current running instance of
+ * IorapForwardingService#IorapdJobService.
+ *
+ * Visibility: Must be accessible from android.app.AppComponentFactory
+ */
+ public static class IorapdJobServiceProxy extends JobService {
+
+ public IorapdJobServiceProxy() {
+ getActualIorapdJobService().bindProxy(this);
+ }
+
+
+ @NonNull
+ private IorapdJobService getActualIorapdJobService() {
+ // Can't ever be null, because the guarantee is that the
+ // IorapForwardingService is always running.
+ // We are in the same process as Job Service.
+ return sSelfService.mJobService;
+ }
+
+ // Called by system to start the job.
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ return getActualIorapdJobService().onStartJob(params);
+ }
+
+ // Called by system to prematurely stop the job.
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return getActualIorapdJobService().onStopJob(params);
+ }
+ }
+
+ private class IorapdJobService extends JobService {
+ private final ComponentName IORAPD_COMPONENT_NAME;
+
+ private final Object mLock = new Object();
+ // Jobs currently running remotely on iorapd.
+ // They were started by the JobScheduler and need to be finished.
+ private final HashMap<RequestId, JobParameters> mRunningJobs = new HashMap<>();
+
+ private final JobInfo IORAPD_JOB_INFO;
+
+ private volatile IorapdJobServiceProxy mProxy;
+
+ public void bindProxy(IorapdJobServiceProxy proxy) {
+ mProxy = proxy;
+ }
+
+ // Create a new job service which immediately schedules a 24-hour idle maintenance mode
+ // background job to execute.
+ public IorapdJobService(Context context) {
+ if (DEBUG) {
+ Log.v(TAG, "IorapdJobService (Context=" + context.toString() + ")");
+ }
+
+ // Schedule the proxy class to be instantiated by the JobScheduler
+ // when it is time to invoke background jobs for IorapForwardingService.
+
+
+ // This also needs a BIND_JOB_SERVICE permission in
+ // frameworks/base/core/res/AndroidManifest.xml
+ IORAPD_COMPONENT_NAME = new ComponentName(context, IorapdJobServiceProxy.class);
+
+ JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_IORAPD, IORAPD_COMPONENT_NAME);
+ builder.setPeriodic(JOB_INTERVAL_MS);
+
+ builder.setRequiresCharging(true);
+ builder.setRequiresDeviceIdle(true);
+
+ builder.setRequiresStorageNotLow(true);
+
+ IORAPD_JOB_INFO = builder.build();
+
+ JobScheduler js = context.getSystemService(JobScheduler.class);
+ js.schedule(IORAPD_JOB_INFO);
+ Log.d(TAG,
+ "BgJob Scheduled (jobId=" + JOB_ID_IORAPD
+ + ", interval: " + JOB_INTERVAL_MS + "ms)");
+ }
+
+ // Called by system to start the job.
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ // Tell iorapd to start a background job.
+ Log.d(TAG, "Starting background job: " + params.toString());
+
+ mAbortIdleCompilation.set(false);
+ // PackageManagerService starts before IORap service.
+ PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+ List<String> pkgs = pm.getAllPackages();
+ runIdleCompilationAsync(params, pkgs);
+ return true;
+ }
+
+ private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) {
+ new Thread("IORap_IdleCompilation") {
+ @Override
+ public void run() {
+ for (int i = 0; i < pkgs.size(); i++) {
+ String pkg = pkgs.get(i);
+ if (mAbortIdleCompilation.get()) {
+ Log.i(TAG, "The idle compilation is aborted");
+ return;
+ }
+
+ // We wait until that job's sequence ID returns to us with 'Completed',
+ RequestId request;
+ synchronized (mLock) {
+ // TODO: would be cleaner if we got the request from the
+ // 'invokeRemote' function. Better yet, consider
+ // a Pair<RequestId, Future<TaskResult>> or similar.
+ request = RequestId.nextValueForSequence();
+ mRunningJobs.put(request, params);
+ }
+
+ Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]",
+ pkg, i + 1, pkgs.size()));
+ boolean shouldUpdateVersions = (i == 0);
+ if (!invokeRemote(mIorapRemote, (IIorap remote) ->
+ remote.onJobScheduledEvent(request,
+ JobScheduledEvent.createIdleMaintenance(
+ JobScheduledEvent.TYPE_START_JOB,
+ params,
+ pkg,
+ shouldUpdateVersions)))) {
+ synchronized (mLock) {
+ mRunningJobs.remove(request); // Avoid memory leaks.
+ }
+ }
+
+ // Wait until the job is complete and removed from the running jobs.
+ retryWithTimeout(TIMEOUT, () -> {
+ synchronized (mLock) {
+ return !mRunningJobs.containsKey(request);
+ }
+ });
+ }
+
+ // Finish the job after all packages are compiled.
+ if (mProxy != null) {
+ mProxy.jobFinished(params, /*reschedule*/false);
+ }
+ }
+ }.start();
+ }
+
+ /** Retry until timeout. */
+ private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) {
+ long totalSleepTimeMs = 0L;
+ long sleepIntervalMs = 10L;
+ while (true) {
+ if (supplier.getAsBoolean()) {
+ return true;
+ }
+ try {
+ TimeUnit.MILLISECONDS.sleep(sleepIntervalMs);
+ } catch (InterruptedException e) {
+ Log.e(TAG, e.getMessage());
+ return false;
+ }
+
+ totalSleepTimeMs += sleepIntervalMs;
+ if (totalSleepTimeMs > timeout.toMillis()) {
+ return false;
+ }
+ }
+ }
+
+ // Called by system to prematurely stop the job.
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // As this is unexpected behavior, print a warning.
+ Log.w(TAG, "onStopJob(params=" + params.toString() + ")");
+ mAbortIdleCompilation.set(true);
+
+ // Yes, retry the job at a later time no matter what.
+ return true;
+ }
+
+ // Listen to *all* task completes for all requests.
+ // The majority of these might be unrelated to background jobs.
+ public void onIorapdTaskCompleted(RequestId requestId) {
+ JobParameters jobParameters;
+ synchronized (mLock) {
+ jobParameters = mRunningJobs.remove(requestId);
+ }
+
+ // Typical case: This was a task callback unrelated to our jobs.
+ if (jobParameters == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("IorapdJobService#onIorapdTaskCompleted(%s), found params=%s",
+ requestId, jobParameters));
+ }
+
+ Log.d(TAG, "Finished background job: " + jobParameters.toString());
+ }
+
+ public void onIorapdDisconnected() {
+ synchronized (mLock) {
+ mRunningJobs.clear();
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, String.format("IorapdJobService#onIorapdDisconnected"));
+ }
+
+ // TODO: should we try to resubmit all incomplete jobs after it's reconnected?
+ }
+ }
+
+ private class RemoteTaskListener extends ITaskListener.Stub {
+ @Override
+ public void onProgress(RequestId requestId, TaskResult result) throws RemoteException {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result));
+ }
+
+ // TODO: implement rest.
+ }
+
+ @Override
+ public void onComplete(RequestId requestId, TaskResult result) throws RemoteException {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result));
+ }
+
+ if (mJobService != null) {
+ mJobService.onIorapdTaskCompleted(requestId);
+ }
+
+ // TODO: implement rest.
+ }
+ }
+
+ /** Allow passing lambdas to #invokeRemote */
+ private interface RemoteRunnable {
+ // TODO: run(RequestId) ?
+ void run(IIorap iorap) throws RemoteException;
+ }
+
+ // Always pass in the iorap directly here to avoid data race.
+ private static boolean invokeRemote(IIorap iorap, RemoteRunnable r) {
+ if (iorap == null) {
+ Log.w(TAG, "IIorap went to null in this thread, drop invokeRemote.");
+ return false;
+ }
+ try {
+ r.run(iorap);
+ return true;
+ } catch (RemoteException e) {
+ // This could be a logic error (remote side returning error), which we need to fix.
+ //
+ // This could also be a DeadObjectException in which case its probably just iorapd
+ // being manually restarted.
+ //
+ // Don't make any assumption, since DeadObjectException could also mean iorapd crashed
+ // unexpectedly.
+ //
+ // DeadObjectExceptions are recovered from using DeathRecipient and #linkToDeath.
+ handleRemoteError(e);
+ return false;
+ }
+ }
+
+ private static void handleRemoteError(Throwable t) {
+ if (WTF_CRASH) {
+ // In development modes, we just want to crash.
+ throw new AssertionError("unexpected remote error", t);
+ } else {
+ // Log to wtf which gets sent to dropbox, and in system_server this does not crash.
+ Log.wtf(TAG, t);
+ }
+ }
+
+ // Encode A-Z bitstring into bits. Every character is bits.
+ // Characters outside of the range [a,z] are considered out of range.
+ //
+ // The least significant bits hold the last character.
+ // First 2 bits are left as 0.
+ private static int encodeEnglishAlphabetStringIntoInt(String name) {
+ int value = 0;
+
+ final int CHARS_PER_INT = 6;
+ final int BITS_PER_CHAR = 5;
+ // Note: 2 top bits are unused, this also means our values are non-negative.
+ final char CHAR_LOWER = 'a';
+ final char CHAR_UPPER = 'z';
+
+ if (name.length() > CHARS_PER_INT) {
+ throw new IllegalArgumentException(
+ "String too long. Cannot encode more than 6 chars: " + name);
+ }
+
+ for (int i = 0; i < name.length(); ++i) {
+ char c = name.charAt(i);
+
+ if (c < CHAR_LOWER || c > CHAR_UPPER) {
+ throw new IllegalArgumentException("String has out-of-range [a-z] chars: " + name);
+ }
+
+ // Avoid sign extension during promotion.
+ int cur_value = (c & 0xFFFF) - (CHAR_LOWER & 0xFFFF);
+ if (cur_value >= (1 << BITS_PER_CHAR)) {
+ throw new AssertionError("wtf? i=" + i + ", name=" + name);
+ }
+
+ value = value << BITS_PER_CHAR;
+ value = value | cur_value;
+ }
+
+ return value;
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
new file mode 100644
index 0000000..b91dd71
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.app.job.JobParameters;
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward JobService events to iorapd. <br /><br />
+ *
+ * iorapd sometimes need to use background jobs. Forwarding these events to iorapd
+ * notifies iorapd when it is an opportune time to execute these background jobs.
+ *
+ * @hide
+ */
+public class JobScheduledEvent implements Parcelable {
+
+ /** JobService#onJobStarted */
+ public static final int TYPE_START_JOB = 0;
+ /** JobService#onJobStopped */
+ public static final int TYPE_STOP_JOB = 1;
+ private static final int TYPE_MAX = 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_START_JOB,
+ TYPE_STOP_JOB,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ /** @see JobParameters#getJobId() */
+ public final int jobId;
+
+ public final String packageName;
+
+ public final boolean shouldUpdateVersions;
+
+ /** Device is 'idle' and it's charging (plugged in). */
+ public static final int SORT_IDLE_MAINTENANCE = 0;
+ private static final int SORT_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SORT_" }, value = {
+ SORT_IDLE_MAINTENANCE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Sort {}
+
+ /**
+ * Roughly corresponds to the {@code extras} fields in a JobParameters.
+ */
+ @Sort public final int sort;
+
+ /**
+ * Creates a {@link #SORT_IDLE_MAINTENANCE} event from the type and job parameters.
+ *
+ * Only the job ID is retained from {@code jobParams}, all other param info is dropped.
+ */
+ @NonNull
+ public static JobScheduledEvent createIdleMaintenance(
+ @Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) {
+ return new JobScheduledEvent(
+ type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions);
+ }
+
+ private JobScheduledEvent(@Type int type,
+ int jobId,
+ @Sort int sort,
+ String packageName,
+ boolean shouldUpdateVersions) {
+ this.type = type;
+ this.jobId = jobId;
+ this.sort = sort;
+ this.packageName = packageName;
+ this.shouldUpdateVersions = shouldUpdateVersions;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ // No check for 'jobId': any int is valid.
+ CheckHelpers.checkTypeInRange(sort, SORT_MAX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof JobScheduledEvent) {
+ return equals((JobScheduledEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(JobScheduledEvent other) {
+ return type == other.type &&
+ jobId == other.jobId &&
+ sort == other.sort &&
+ packageName.equals(other.packageName) &&
+ shouldUpdateVersions == other.shouldUpdateVersions;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}",
+ type, jobId, sort, packageName, shouldUpdateVersions);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ out.writeInt(jobId);
+ out.writeInt(sort);
+ out.writeString(packageName);
+ out.writeBoolean(shouldUpdateVersions);
+
+ // We do not parcel the entire JobParameters here because there is no C++ equivalent
+ // of that class [which the iorapd side of the binder interface requires].
+ }
+
+ private JobScheduledEvent(Parcel in) {
+ this.type = in.readInt();
+ this.jobId = in.readInt();
+ this.sort = in.readInt();
+ this.packageName = in.readString();
+ this.shouldUpdateVersions = in.readBoolean();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<JobScheduledEvent> CREATOR
+ = new Parcelable.Creator<JobScheduledEvent>() {
+ public JobScheduledEvent createFromParcel(Parcel in) {
+ return new JobScheduledEvent(in);
+ }
+
+ public JobScheduledEvent[] newArray(int size) {
+ return new JobScheduledEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
new file mode 100644
index 0000000..aa4eea7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Forward package manager events to iorapd. <br /><br />
+ *
+ * Knowing when packages are modified by the system are a useful tidbit to help with performance:
+ * for example when a package is replaced, it could be a hint used to invalidate any collected
+ * io profiles used for prefetching or pinning.
+ *
+ * @hide
+ */
+public class PackageEvent implements Parcelable {
+
+ /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */
+ public static final int TYPE_REPLACED = 0;
+ private static final int TYPE_MAX = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_REPLACED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */
+ public final Uri packageUri;
+ /** The name of the package, for example {@code com.android.calculator}. */
+ public final String packageName;
+
+ @NonNull
+ public static PackageEvent createReplaced(Uri packageUri, String packageName) {
+ return new PackageEvent(TYPE_REPLACED, packageUri, packageName);
+ }
+
+ private PackageEvent(@Type int type, Uri packageUri, String packageName) {
+ this.type = type;
+ this.packageUri = packageUri;
+ this.packageName = packageName;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ Objects.requireNonNull(packageUri, "packageUri");
+ Objects.requireNonNull(packageName, "packageName");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof PackageEvent) {
+ return equals((PackageEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(PackageEvent other) {
+ return type == other.type &&
+ Objects.equals(packageUri, other.packageUri) &&
+ Objects.equals(packageName, other.packageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ packageUri.writeToParcel(out, flags);
+ out.writeString(packageName);
+ }
+
+ private PackageEvent(Parcel in) {
+ this.type = in.readInt();
+ this.packageUri = Uri.CREATOR.createFromParcel(in);
+ this.packageName = in.readString();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<PackageEvent> CREATOR
+ = new Parcelable.Creator<PackageEvent>() {
+ public PackageEvent createFromParcel(Parcel in) {
+ return new PackageEvent(in);
+ }
+
+ public PackageEvent[] newArray(int size) {
+ return new PackageEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
new file mode 100644
index 0000000..503e1c6
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.NonNull;
+
+/**
+ * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * used for asynchronous callbacks by the server. <br /><br />
+ *
+ * As all system server binder calls must be {@code oneway}, this means all invocations
+ * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID
+ * exists to associate all calls with their respective callbacks in
+ * {@link com.google.android.startop.iorap.ITaskListener}.
+ *
+ * @see com.google.android.startop.iorap.IIorap
+ *
+ * @hide
+ */
+public class RequestId implements Parcelable {
+
+ public final long requestId;
+
+ private static Object mLock = new Object();
+ private static long mNextRequestId = 0;
+
+ /**
+ * Create a monotonically increasing request ID.<br /><br />
+ *
+ * It is invalid to re-use the same request ID for multiple method calls on
+ * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created
+ * each time.
+ */
+ @NonNull public static RequestId nextValueForSequence() {
+ long currentRequestId;
+ synchronized (mLock) {
+ currentRequestId = mNextRequestId;
+ ++mNextRequestId;
+ }
+ return new RequestId(currentRequestId);
+ }
+
+ private RequestId(long requestId) {
+ this.requestId = requestId;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ if (requestId < 0) {
+ throw new IllegalArgumentException("request id must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{requestId: %d}", requestId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(requestId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof RequestId) {
+ return equals((RequestId) other);
+ }
+ return false;
+ }
+
+ private boolean equals(RequestId other) {
+ return requestId == other.requestId;
+ }
+
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(requestId);
+ }
+
+ private RequestId(Parcel in) {
+ requestId = in.readLong();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<RequestId> CREATOR
+ = new Parcelable.Creator<RequestId>() {
+ public RequestId createFromParcel(Parcel in) {
+ return new RequestId(in);
+ }
+
+ public RequestId[] newArray(int size) {
+ return new RequestId[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
new file mode 100644
index 0000000..75d47f9
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward system service events to iorapd.
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onBootPhase */
+ public static final int TYPE_BOOT_PHASE = 0;
+ /** @see com.android.server.SystemService#onStart */
+ public static final int TYPE_START = 1;
+ private static final int TYPE_MAX = TYPE_START;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_BOOT_PHASE,
+ TYPE_START,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+
+ // TODO: do we want to pass the exact build phase enum?
+
+ public SystemServiceEvent(@Type int type) {
+ this.type = type;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d}", type);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceEvent) {
+ return equals((SystemServiceEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceEvent other) {
+ return type == other.type;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ }
+
+ private SystemServiceEvent(Parcel in) {
+ this.type = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceEvent>() {
+ public SystemServiceEvent createFromParcel(Parcel in) {
+ return new SystemServiceEvent(in);
+ }
+
+ public SystemServiceEvent[] newArray(int size) {
+ return new SystemServiceEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
new file mode 100644
index 0000000..2e7bafe
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward user events to iorapd.<br /><br />
+ *
+ * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies
+ * by iorapd (e.g. to handle user default pinned applications changing).
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceUserEvent implements Parcelable {
+
+ /** @see com.android.server.SystemService#onUserStarting */
+ public static final int TYPE_START_USER = 0;
+ /** @see com.android.server.SystemService#onUserUnlocking */
+ public static final int TYPE_UNLOCK_USER = 1;
+ /** @see com.android.server.SystemService#onUserSwitching*/
+ public static final int TYPE_SWITCH_USER = 2;
+ /** @see com.android.server.SystemService#onUserStopping */
+ public static final int TYPE_STOP_USER = 3;
+ /** @see com.android.server.SystemService#onUserStopped */
+ public static final int TYPE_CLEANUP_USER = 4;
+ private static final int TYPE_MAX = TYPE_CLEANUP_USER;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_START_USER,
+ TYPE_UNLOCK_USER,
+ TYPE_SWITCH_USER,
+ TYPE_STOP_USER,
+ TYPE_CLEANUP_USER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ @Type public final int type;
+ public final int userHandle;
+
+ public SystemServiceUserEvent(@Type int type, int userHandle) {
+ this.type = type;
+ this.userHandle = userHandle;
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+ if (userHandle < 0) {
+ throw new IllegalArgumentException("userHandle must be non-negative");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{type: %d, userHandle: %d}", type, userHandle);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof SystemServiceUserEvent) {
+ return equals((SystemServiceUserEvent) other);
+ }
+ return false;
+ }
+
+ private boolean equals(SystemServiceUserEvent other) {
+ return type == other.type &&
+ userHandle == other.userHandle;
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(type);
+ out.writeInt(userHandle);
+ }
+
+ private SystemServiceUserEvent(Parcel in) {
+ this.type = in.readInt();
+ this.userHandle = in.readInt();
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR
+ = new Parcelable.Creator<SystemServiceUserEvent>() {
+ public SystemServiceUserEvent createFromParcel(Parcel in) {
+ return new SystemServiceUserEvent(in);
+ }
+
+ public SystemServiceUserEvent[] newArray(int size) {
+ return new SystemServiceUserEvent[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
new file mode 100644
index 0000000..b5fd6d8
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener}
+ * callbacks.<br /><br />
+ *
+ * Following {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br />
+ *
+ * State transitions are as follows: <br /><br />
+ *
+ * <pre>
+ * ┌─────────────────────────────┐
+ * │ ▼
+ * ┌───────┐ ┌─────────┐ ╔═══════════╗
+ * ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║
+ * └───────┘ └─────────┘ ╚═══════════╝
+ * │ │
+ * │ │
+ * ▼ │
+ * ╔═══════╗ │
+ * ──▶ ║ ERROR ║ ◀─────┘
+ * ╚═══════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/TaskResult.dot -->
+ *
+ * @hide
+ */
+public class TaskResult implements Parcelable {
+
+ public static final int STATE_BEGAN = 0;
+ public static final int STATE_ONGOING = 1;
+ public static final int STATE_COMPLETED = 2;
+ public static final int STATE_ERROR = 3;
+ private static final int STATE_MAX = STATE_ERROR;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_BEGAN,
+ STATE_ONGOING,
+ STATE_COMPLETED,
+ STATE_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ @State public final int state;
+
+ @Override
+ public String toString() {
+ return String.format("{state: %d}", state);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof TaskResult) {
+ return equals((TaskResult) other);
+ }
+ return false;
+ }
+
+ private boolean equals(TaskResult other) {
+ return state == other.state;
+ }
+
+ public TaskResult(@State int state) {
+ this.state = state;
+
+ checkConstructorArguments();
+ }
+
+ private void checkConstructorArguments() {
+ CheckHelpers.checkStateInRange(state, STATE_MAX);
+ }
+
+ //<editor-fold desc="Binder boilerplate">
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(state);
+ }
+
+ private TaskResult(Parcel in) {
+ state = in.readInt();
+
+ checkConstructorArguments();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<TaskResult> CREATOR
+ = new Parcelable.Creator<TaskResult>() {
+ public TaskResult createFromParcel(Parcel in) {
+ return new TaskResult(in);
+ }
+
+ public TaskResult[] newArray(int size) {
+ return new TaskResult[size];
+ }
+ };
+ //</editor-fold>
+}
diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp
new file mode 100644
index 0000000..6e8725d
--- /dev/null
+++ b/startop/iorap/stress/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2020 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_binary {
+ name: "iorap.stress.memory",
+ srcs: ["main_memory.cc"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter"
+ ],
+
+ shared_libs: [
+ "libbase"
+ ],
+
+ host_supported: true,
+}
diff --git a/startop/iorap/stress/main_memory.cc b/startop/iorap/stress/main_memory.cc
new file mode 100644
index 0000000..1f26861
--- /dev/null
+++ b/startop/iorap/stress/main_memory.cc
@@ -0,0 +1,126 @@
+//
+// Copyright (C) 2020 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.
+//
+
+#include <chrono>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <string>
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include <android-base/parseint.h>
+
+static constexpr size_t kBytesPerMb = 1048576;
+const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb;
+
+#define USE_MLOCKALL 0
+
+std::string GetProcessStatus(const char* key) {
+ // Build search pattern of key and separator.
+ std::string pattern(key);
+ pattern.push_back(':');
+
+ // Search for status lines starting with pattern.
+ std::ifstream fs("/proc/self/status");
+ std::string line;
+ while (std::getline(fs, line)) {
+ if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) {
+ // Skip whitespace in matching line (if any).
+ size_t pos = line.find_first_not_of(" \t", pattern.size());
+ if (pos == std::string::npos) {
+ break;
+ }
+ return std::string(line, pos);
+ }
+ }
+ return "<unknown>";
+}
+
+int main(int argc, char** argv) {
+ size_t allocationSize = 0;
+ if (argc >= 2) {
+ if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) {
+ std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl;
+ return 1;
+ }
+ } else {
+ allocationSize = kMemoryAllocationSize;
+ }
+
+ void* mem = malloc(allocationSize);
+ if (mem == nullptr) {
+ std::cerr << "Malloc failed" << std::endl;
+ return 1;
+ }
+
+ volatile int* imem = static_cast<int *>(mem); // don't optimize out memory usage
+
+ size_t imemCount = allocationSize / sizeof(int);
+
+ std::cout << "Allocated " << allocationSize << " bytes" << std::endl;
+
+ auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
+ std::mt19937 mt_rand(seed);
+
+ size_t randPrintCount = 10;
+
+ // Write random numbers:
+ // * Ensures each page is resident
+ // * Avoids zeroed out pages (zRAM)
+ // * Avoids same-page merging
+ for (size_t i = 0; i < imemCount; ++i) {
+ imem[i] = mt_rand();
+
+ if (i < randPrintCount) {
+ std::cout << "Generated random value: " << imem[i] << std::endl;
+ }
+ }
+
+#if USE_MLOCKALL
+ /*
+ * Lock all pages from the address space of this process.
+ */
+ if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
+ std::cerr << "Mlockall failed" << std::endl;
+ return 1;
+ }
+#else
+ // Use mlock because of the predictable VmLck size.
+ // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device.
+ if (mlock(mem, allocationSize) != 0) {
+ std::cerr << "Mlock failed" << std::endl;
+ return 1;
+ }
+#endif
+
+ // Validate memory is actually resident and locked with:
+ // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck
+ std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl;
+
+ std::cout << "Press any key to terminate" << std::endl;
+ int any_input;
+ std::cin >> any_input;
+
+ std::cout << "Terminating..." << std::endl;
+
+ munlockall();
+ free(mem);
+
+ return 0;
+}
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
new file mode 100644
index 0000000..ad3d001
--- /dev/null
+++ b/startop/iorap/tests/Android.bp
@@ -0,0 +1,72 @@
+// Copyright (C) 2018 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.
+
+// TODO: once b/80095087 is fixed, rewrite this back to android_test
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "libiorap-java-test-lib",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ // Non-test dependencies
+ // library under test
+ "services.startop.iorap",
+ // need the system_server code to be on the classpath,
+ "services.core",
+ // Test Dependencies
+ // test android dependencies
+ "platform-test-annotations",
+ "androidx.test.rules",
+ // test framework dependencies
+ "mockito-target-inline-minus-junit4",
+ // "mockito-target-minus-junit4",
+ // Mockito also requires JNI (see Android.mk)
+ // and android:debuggable=true (see AndroidManifest.xml)
+ "truth-prebuilt",
+ ],
+ // sdk_version: "current",
+ // certificate: "platform",
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ // test_suites: ["device-tests"],
+}
+
+android_test {
+ name: "libiorap-java-tests",
+ dxflags: ["--multi-dex"],
+ test_suites: ["device-tests"],
+ static_libs: ["libiorap-java-test-lib"],
+ compile_multilib: "both",
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ // Use private APIs
+ certificate: "platform",
+ platform_apis: true,
+}
diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b967e72
--- /dev/null
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.startop.iorap.tests"
+ android:sharedUserId="com.google.android.startop.iorap.tests"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <!--suppress AndroidDomInspection -->
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.google.android.startop.iorap.tests" />
+
+ <!--
+ 'debuggable=true' is required to properly load mockito jvmti dependencies,
+ otherwise it gives the following error at runtime:
+
+ Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+ Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/startop/iorap/tests/AndroidTest.xml b/startop/iorap/tests/AndroidTest.xml
new file mode 100644
index 0000000..6102c44
--- /dev/null
+++ b/startop/iorap/tests/AndroidTest.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<configuration description="Runs libiorap-java-tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="libiorap-java-tests.apk" />
+ </target_preparer>
+
+ <!--
+ Our IIorapIntegrationTest.kt requires setlinux to be disabled:
+ it connects to the iorapd binder service but this requires selinux permissions:
+
+ avc: denied { find } for service=iorapd pid=2738 uid=10050
+ scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:iorapd_service:s0
+ tclass=service_manager permissive=0
+ -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer">
+ </target_preparer>
+
+ <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
+ furthermore the changes in /data/local.prop don't actually seem to get picked up.
+ -->
+ <target_preparer
+ class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
+ <option name="force-skip-system-props" value="true" />
+
+ <!-- Crash instead of using Log.wtf within the system_server iorap code. -->
+ <option name="run-command" value="setprop iorapd.forwarding_service.wtf_crash true" />
+ <!-- IIorapd has fake behavior: it doesn't do anything but reply with 'DONE' status -->
+ <option name="run-command" value="setprop iorapd.binder.fake true" />
+
+ <!-- iorapd does not pick up the above changes until we restart it -->
+ <option name="run-command" value="stop iorapd" />
+ <option name="run-command" value="start iorapd" />
+ <!-- give it some time to restart the service; otherwise the first unit test might fail -->
+ <option name="run-command" value="sleep 1" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.google.android.startop.iorap.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+
+ <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad
+ state. there is no way to clean this up as far as I know.
+ -->
+
+</configuration>
+
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt
new file mode 100644
index 0000000..51e407d
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.google.android.startop.iorap
+
+import android.content.Intent;
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import com.google.android.startop.iorap.AppLaunchEvent;
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunched
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchCancelled
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchFinished
+import com.google.android.startop.iorap.AppLaunchEvent.IntentStarted;
+import com.google.android.startop.iorap.AppLaunchEvent.IntentFailed;
+import com.google.android.startop.iorap.AppLaunchEvent.ReportFullyDrawn
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+
+/**
+ * Basic unit tests to test all of the [AppLaunchEvent]s in [com.google.android.startop.iorap].
+ */
+@SmallTest
+class AppLaunchEventTest {
+ /**
+ * Test for IntentStarted.
+ */
+ @Test
+ fun testIntentStarted() {
+ var intent = Intent()
+ val valid = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L)
+ val copy = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L)
+ val noneCopy1 = IntentStarted(/* sequenceId= */1L, intent, /* timestampNs= */ 1L)
+ val noneCopy2 = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 2L)
+ val noneCopy3 = IntentStarted(/* sequenceId= */2L, Intent(), /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("IntentStarted{sequenceId=2, intent=Intent { } , timestampNs=1}")
+ }
+
+ /**
+ * Test for IntentFailed.
+ */
+ @Test
+ fun testIntentFailed() {
+ val valid = IntentFailed(/* sequenceId= */2L)
+ val copy = IntentFailed(/* sequenceId= */2L)
+ val noneCopy = IntentFailed(/* sequenceId= */1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("IntentFailed{sequenceId=2}")
+ }
+
+ /**
+ * Test for ActivityLaunched.
+ */
+ @Test
+ fun testActivityLaunched() {
+ //var activityRecord =
+ val valid = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val copy = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val noneCopy1 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val noneCopy2 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(),
+ /* temperature= */ 1)
+ val noneCopy3 = ActivityLaunched(/* sequenceId= */1L, "test1".toByteArray(),
+ /* temperature= */ 0)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunched{sequenceId=2, test, temperature=0}")
+ }
+
+
+ /**
+ * Test for ActivityLaunchFinished.
+ */
+ @Test
+ fun testActivityLaunchFinished() {
+ val valid = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val copy = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy1 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy2 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 2L)
+ val noneCopy3 = ActivityLaunchFinished(/* sequenceId= */2L, "test1".toByteArray(),
+ /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunchFinished{sequenceId=2, test, timestampNs=1}")
+ }
+
+ /**
+ * Test for ActivityLaunchCancelled.
+ */
+ @Test
+ fun testActivityLaunchCancelled() {
+ val valid = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray())
+ val copy = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray())
+ val noneCopy1 = ActivityLaunchCancelled(/* sequenceId= */1L, "test".toByteArray())
+ val noneCopy2 = ActivityLaunchCancelled(/* sequenceId= */2L, "test1".toByteArray())
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunchCancelled{sequenceId=2, test}")
+ }
+
+ /**
+ * Test for ReportFullyDrawn.
+ */
+ @Test
+ fun testReportFullyDrawn() {
+ val valid = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L)
+ val copy = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L)
+ val noneCopy1 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy2 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy3 = ReportFullyDrawn(/* sequenceId= */2L, "test1".toByteArray(),
+ /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ReportFullyDrawn{sequenceId=2, test, timestampNs=1}")
+ }
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
new file mode 100644
index 0000000..18c2491
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.ServiceManager
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.MediumTest
+import org.junit.Test
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.timeout
+
+// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
+@MediumTest
+@FlakyTest(bugId = 149098310) // Failing on cuttlefish with SecurityException.
+class IIorapIntegrationTest {
+ /**
+ * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
+ */
+ private val iorapService: IIorap by lazy {
+ // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
+ IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
+
+ // Use 'adb shell setenforce 0' otherwise this whole test fails,
+ // because the servicemanager is not allowed to hand out the binder token for iorapd.
+
+ // TODO: implement the selinux policies for iorapd.
+ }
+
+ // A dummy binder stub implementation is required to use with mockito#spy.
+ // Mockito overrides the methods at runtime and tracks how methods were invoked.
+ open class DummyTaskListener : ITaskListener.Stub() {
+ // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
+ // from using the mockito matchers (eq, argThat, etc).
+ override fun onProgress(requestId: RequestId?, result: TaskResult?) {
+ }
+
+ override fun onComplete(requestId: RequestId?, result: TaskResult?) {
+ }
+ }
+
+ private fun testAnyMethod(func: (RequestId) -> Unit) {
+ val taskListener = spy(DummyTaskListener())!!
+
+ // FIXME: b/149098310
+ return
+
+ try {
+ iorapService.setTaskListener(taskListener)
+ // Note: Binder guarantees total order for oneway messages sent to the same binder
+ // interface, so we don't need any additional blocking here before sending later calls.
+
+ // Every new method call should have a unique request id.
+ val requestId = RequestId.nextValueForSequence()!!
+
+ // Apply the specific function under test.
+ func(requestId)
+
+ // Typical mockito behavior is to allow any-order callbacks, but we want to test order.
+ val inOrder = inOrder(taskListener)
+
+ // The "stub" behavior of iorapd is that every request immediately gets a response of
+ // BEGAN,ONGOING,COMPLETED
+ inOrder.verify(taskListener, timeout(100))
+ .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+ inOrder.verify(taskListener, timeout(100))
+ .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+ inOrder.verify(taskListener, timeout(100))
+ .onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+ inOrder.verifyNoMoreInteractions()
+ } finally {
+ // iorapService.setTaskListener(null)
+ // FIXME: null is broken, C++ side sees a non-null object.
+ }
+ }
+
+ @Test
+ fun testOnPackageEvent() {
+ // FIXME (b/137134253): implement PackageEvent parsing on the C++ side.
+ // This is currently (silently: b/137135024) failing because IIorap is 'oneway' and the
+ // C++ PackageEvent un-parceling fails since its not implemented fully.
+ /*
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onPackageEvent(requestId,
+ PackageEvent.createReplaced(
+ Uri.parse("https://www.google.com"), "com.fake.package"))
+ }
+ */
+ }
+
+ @Test
+ fun testOnAppIntentEvent() {
+ testAnyMethod { requestId: RequestId ->
+ iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
+ ActivityInfo("dont care", "dont care"),
+ ActivityInfo("dont care 2", "dont care 2")))
+ }
+ }
+
+ @Test
+ fun testOnAppLaunchEvent() {
+ testAnyMethod { requestId : RequestId ->
+ iorapService.onAppLaunchEvent(requestId, AppLaunchEvent.IntentFailed(/*sequenceId*/123))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceEvent() {
+ testAnyMethod { requestId: RequestId ->
+ iorapService.onSystemServiceEvent(requestId,
+ SystemServiceEvent(SystemServiceEvent.TYPE_START))
+ }
+ }
+
+ @Test
+ fun testOnSystemServiceUserEvent() {
+ testAnyMethod { requestId: RequestId ->
+ iorapService.onSystemServiceUserEvent(requestId,
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 0))
+ }
+ }
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
new file mode 100644
index 0000000..150577a
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import org.junit.runners.Parameterized
+
+/**
+ * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap]
+ * have a valid-conforming interface implementation.
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ParcelablesTest<T : Parcelable>(private val inputData: InputData<T>) {
+ companion object {
+ private val initialRequestId = RequestId.nextValueForSequence()!!
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun data() = listOf(
+ InputData(
+ newActivityInfo(),
+ newActivityInfo(),
+ ActivityInfo("some package", "some other activity")),
+ InputData(
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+ ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED,
+ newActivityInfo())),
+ InputData(
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+ newActivityInfoOther()),
+ AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(),
+ newActivityInfo())),
+ InputData(
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some package"),
+ PackageEvent.createReplaced(newUri(), "some other package")
+ ),
+ InputData(initialRequestId, cloneRequestId(initialRequestId),
+ RequestId.nextValueForSequence()),
+ InputData(
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+ SystemServiceEvent(SystemServiceEvent.TYPE_START)),
+ InputData(
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+ SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)),
+ InputData(
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_COMPLETED),
+ TaskResult(TaskResult.STATE_ONGOING))
+ )
+
+ private fun newActivityInfo(): ActivityInfo {
+ return ActivityInfo("some package", "some activity")
+ }
+
+ private fun newActivityInfoOther(): ActivityInfo {
+ return ActivityInfo("some package 2", "some activity 2")
+ }
+
+ private fun newUri(): Uri {
+ return Uri.parse("https://www.google.com")
+ }
+
+ private fun cloneRequestId(requestId: RequestId): RequestId {
+ val constructor = requestId::class.java.declaredConstructors[0]
+ constructor.isAccessible = true
+ return constructor.newInstance(requestId.requestId) as RequestId
+ }
+ }
+
+ /**
+ * Test for [Object.equals] implementation.
+ */
+ @Test
+ fun testEquality() {
+ assertThat(inputData.valid).isEqualTo(inputData.valid)
+ assertThat(inputData.valid).isEqualTo(inputData.validCopy)
+ assertThat(inputData.valid).isNotEqualTo(inputData.validOther)
+ }
+
+ /**
+ * Test for [Parcelable] implementation.
+ */
+ @Test
+ fun testParcelRoundTrip() {
+ // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
+ val assertParcels = { it: T, data: InputData<T> ->
+ val parcel = Parcel.obtain()
+ it.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0) // future reads will see all previous writes.
+ assertThat(it).isEqualTo(data.createFromParcel(parcel))
+ parcel.recycle()
+ }
+
+ assertParcels(inputData.valid, inputData)
+ assertParcels(inputData.validCopy, inputData)
+ assertParcels(inputData.validOther, inputData)
+ }
+
+ data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) {
+ val kls = valid.javaClass
+ init {
+ assertThat(valid).isNotSameInstanceAs(validCopy)
+ // Don't use isInstanceOf because of phantom warnings in intellij about Class!
+ assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
+ assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
+ }
+
+ fun createFromParcel(parcel: Parcel): T {
+ val field = kls.getDeclaredField("CREATOR")
+ val creator = field.get(null) as Parcelable.Creator<T>
+
+ return creator.createFromParcel(parcel)
+ }
+ }
+}
diff --git a/startop/scripts/app_startup/analyze_metrics.py b/startop/scripts/app_startup/analyze_metrics.py
new file mode 100755
index 0000000..d74d6f6
--- /dev/null
+++ b/startop/scripts/app_startup/analyze_metrics.py
@@ -0,0 +1,457 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+
+"""
+Perform statistical analysis on measurements produced by app_startup_runner.py
+
+Install:
+$> sudo apt-get install python3-scipy
+
+Usage:
+$> ./analyze_metrics.py <filename.csv> [<filename2.csv> ...]
+$> ./analyze_metrics.py --help
+"""
+
+import argparse
+import csv
+import itertools
+import os
+import subprocess
+import sys
+import tempfile
+from typing import Any, List, Dict, Iterable, TextIO, Tuple
+
+from scipy import stats as sc
+import numpy as np
+
+
+# These CSV columns are considered labels. Everything after them in the same row are metrics.
+_LABEL_COLUMNS=['packages', 'readaheads', 'compiler_filters']
+# The metric series with the 'cold' readahead is the baseline.
+# All others (warm, jit, etc) are the potential improvements.
+
+#fixme: this should probably be an option
+_BASELINE=('readaheads', 'cold')
+# ignore this for some statistic calculations
+_IGNORE_PAIR=('readaheads', 'warm')
+_PLOT_SUBKEY='readaheads'
+_PLOT_GROUPKEY='packages'
+_PLOT_DATA_INDEX = 0
+_DELTA=50
+_DELTA2=100
+_PVALUE_THRESHOLD=0.10
+_debug = False # See -d/--debug flag.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Perform statistical analysis on measurements produced by app_start_runner.py.")
+ parser.add_argument('input_files', metavar='file.csv', nargs='+', help='CSV file produced by app_startup_runner.py')
+
+ parser.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output')
+ parser.add_argument('-os', '--output-samples', dest='output_samples', default='/dev/null', action='store', help='Store CSV for per-sample data')
+ parser.add_argument('-oc', '--output-comparable', dest='output_comparable', default='/dev/null', action='store', help='Output CSV for comparable against baseline')
+ parser.add_argument('-ocs', '--output-comparable-significant', dest='output_comparable_significant', default='/dev/null', action='store', help='Output CSV for comparable against baseline (significant only)')
+ parser.add_argument('-pt', '--pvalue-threshold', dest='pvalue_threshold', type=float, default=_PVALUE_THRESHOLD, action='store')
+ parser.add_argument('-dt', '--delta-threshold', dest='delta_threshold', type=int, default=_DELTA, action='store')
+
+ return parser.parse_args(argv)
+
+def _debug_print(*args, **kwargs):
+ """Print the args to sys.stderr if the --debug/-d flag was passed in."""
+ global _debug
+ if _debug:
+ print(*args, **kwargs, file=sys.stderr)
+
+def _expand_gen_repr(args):
+ new_args_list = []
+ for i in args:
+ # detect iterable objects that do not have their own override of __str__
+ if hasattr(i, '__iter__'):
+ to_str = getattr(i, '__str__')
+ if to_str.__objclass__ == object:
+ # the repr for a generator is just type+address, expand it out instead.
+ new_args_list.append([_expand_gen_repr([j])[0] for j in i])
+ continue
+ # normal case: uses the built-in to-string
+ new_args_list.append(i)
+ return new_args_list
+
+def _debug_print_gen(*args, **kwargs):
+ """Like _debug_print but will turn any iterable args into a list."""
+ if not _debug:
+ return
+
+ new_args_list = _expand_gen_repr(args)
+ _debug_print(*new_args_list, **kwargs)
+
+def read_headers(input_file: TextIO) -> Tuple[List[str], List[str]]:
+ _debug_print("read_headers for file: ", input_file.name)
+ csv_reader = csv.reader(input_file)
+
+ label_num_columns = len(_LABEL_COLUMNS)
+
+ try:
+ header = next(csv_reader)
+ except StopIteration:
+ header = None
+ _debug_print('header', header)
+
+ if not header:
+ return (None, None)
+
+ labels = header[0:label_num_columns]
+ data = header[label_num_columns:]
+
+ return (labels, data)
+
+def read_labels_and_data(input_file: TextIO) -> Iterable[Tuple[List[str], List[int]]]:
+ _debug_print("print_analysis for file: ", input_file.name)
+ csv_reader = csv.reader(input_file)
+
+ # Skip the header because it doesn't contain any data.
+ # To get the header see read_headers function.
+ try:
+ header = next(csv_reader)
+ except StopIteration:
+ header = None
+
+ label_num_columns = len(_LABEL_COLUMNS)
+
+ for row in csv_reader:
+ if len(row) > 0 and row[0][0] == ';':
+ _debug_print("skip comment line", row)
+ continue
+
+ labels = row[0:label_num_columns]
+ data = [int(i) for i in row[label_num_columns:]]
+
+# _debug_print("labels:", labels)
+# _debug_print("data:", data)
+
+ yield (labels, data)
+
+def group_metrics_by_label(it: Iterable[Tuple[List[str], List[int]]]):
+ prev_labels = None
+ data_2d = []
+
+ for label_list, data_list in it:
+ if prev_labels != label_list:
+ if prev_labels:
+# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d)
+ yield (prev_labels, data_2d)
+ data_2d = []
+
+ data_2d.append(data_list)
+ prev_labels = label_list
+
+ if prev_labels:
+# _debug_print("grouped labels:", prev_labels, "data_2d:", data_2d)
+ yield (prev_labels, data_2d)
+
+def data_to_numpy(it: Iterable[Tuple[List[str], List[List[int]]]]) -> Iterable[Tuple[List[str], Any]]:
+ for label_list, data_2d in it:
+ yield (label_list, np.asarray(data_2d, dtype=int))
+
+def iterate_columns(np_data_2d):
+ for col in range(np_data_2d.shape[1]):
+ col_as_array = np_data_2d[:, col]
+ yield col_as_array
+
+def confidence_interval(np_data_2d, percent=0.95):
+ """
+ Given some data [[a,b,c],[d,e,f,]...]
+
+ We assume the same metric is in the column (e.g. [a,d])
+ and that data in the rows (e.g. [b,e]) are separate metric values.
+
+ We then calculate the CI for each metric individually returning it as a list of tuples.
+ """
+ arr = []
+ for col_2d in iterate_columns(np_data_2d):
+ mean = col_2d.mean()
+ sigma = col_2d.std()
+
+ ci = sc.norm.interval(percent, loc=mean, scale=sigma / np.sqrt(len(col_2d)))
+ arr.append(ci)
+
+ # TODO: This seems to be returning NaN when all the samples have the same exact value
+ # (e.g. stddev=0, which can trivially happen when sample count = 1).
+
+ return arr
+
+def print_analysis(it, label_header: List[str], data_header: List[str], output_samples: str):
+ print(label_header)
+
+ with open(output_samples, "w") as output_file:
+
+ csv_writer = csv.writer(output_file)
+ csv_writer.writerow(label_header + ['mean', 'std', 'confidence_interval_a', 'confidence_interval_b'])
+
+ for label_list, np_data_2d in it:
+ print("**********************")
+ print(label_list)
+ print()
+ print(" ", data_header)
+ # aggregate computation column-wise
+ print("Mean: ", np_data_2d.mean(axis=0))
+ print("Std: ", np_data_2d.std(axis=0))
+ print("CI95%:", confidence_interval(np_data_2d))
+ print("SEM: ", stats_standard_error_one(np_data_2d, axis=0))
+
+ #ci = confidence_interval(np_data_2d)[_PLOT_DATA_INDEX]
+ sem = stats_standard_error_one(np_data_2d, axis=0)[_PLOT_DATA_INDEX]
+ mean = np_data_2d.mean(axis=0)[_PLOT_DATA_INDEX]
+
+ ci = (mean - sem, mean + sem)
+
+ csv_writer.writerow(label_list + [mean, np_data_2d.std(axis=0)[_PLOT_DATA_INDEX], ci[0], ci[1]])
+
+def from_file_group_by_labels(input_file):
+ (label_header, data_header) = read_headers(input_file)
+ label_data_iter = read_labels_and_data(input_file)
+ grouped_iter = group_metrics_by_label(label_data_iter)
+ grouped_numpy_iter = data_to_numpy(grouped_iter)
+
+ return grouped_numpy_iter, label_header, data_header
+
+def list_without_index(list, index):
+ return list[:index] + list[index+1:]
+
+def group_by_without_baseline_key(grouped_numpy_iter, label_header):
+ """
+ Data is considered comparable if the only difference is the baseline key
+ (i.e. the readahead is different but the package, compilation filter, etc, are the same).
+
+ Returns iterator that's grouped by the non-baseline labels to an iterator of
+ (label_list, data_2d).
+ """
+ baseline_index = label_header.index(_BASELINE[0])
+
+ def get_label_without_baseline(tpl):
+ label_list, _ = tpl
+ return list_without_index(label_list, baseline_index)
+ # [['pkgname', 'compfilter', 'warm'], [data]]
+ # [['pkgname', 'compfilter', 'cold'], [data2]]
+ # [['pkgname2', 'compfilter', 'warm'], [data3]]
+ #
+ # ->
+ # ( [['pkgname', 'compfilter', 'warm'], [data]] # ignore baseline label change.
+ # [['pkgname', 'compfilter', 'cold'], [data2]] ), # split here because the pkgname changed.
+ # ( [['pkgname2', 'compfilter', 'warm'], [data3]] )
+ for group_info, it in itertools.groupby(grouped_numpy_iter, key = get_label_without_baseline):
+ yield it
+
+ # TODO: replace this messy manual iteration/grouping with pandas
+
+def iterate_comparable_metrics(without_baseline_iter, label_header):
+ baseline_index = label_header.index(_BASELINE[0])
+ baseline_value = _BASELINE[1]
+
+ _debug_print("iterate comparables")
+
+ def is_baseline_fun(tp):
+ ll, dat = tp
+ return ll[baseline_index] == baseline_value
+
+ # iterating here when everything but the baseline key is the same.
+ for it in without_baseline_iter:
+ it1, it2 = itertools.tee(it)
+
+ # find all the baseline data.
+ baseline_filter_it = filter(is_baseline_fun, it1)
+
+ # find non-baseline data.
+ nonbaseline_filter_it = itertools.filterfalse(is_baseline_fun, it2)
+
+ yield itertools.product(baseline_filter_it, nonbaseline_filter_it)
+
+def stats_standard_error_one(a, axis):
+ a_std = a.std(axis=axis, ddof=0)
+ a_len = a.shape[axis]
+
+ return a_std / np.sqrt(a_len)
+
+def stats_standard_error(a, b, axis):
+ a_std = a.std(axis=axis, ddof=0)
+ b_std = b.std(axis=axis, ddof=0)
+
+ a_len = a.shape[axis]
+ b_len = b.shape[axis]
+
+ temp1 = a_std*a_std/a_len
+ temp2 = b_std*b_std/b_len
+
+ return np.sqrt(temp1 + temp2)
+
+def stats_tvalue(a, b, axis, delta = 0):
+ a_mean = a.mean(axis=axis)
+ b_mean = b.mean(axis=axis)
+
+ return (a_mean - b_mean - delta) / stats_standard_error(a, b, axis)
+
+def stats_pvalue(a, b, axis, delta, left:bool = False):
+ """
+ Single-tailed 2-sample t-test.
+
+ Returns p-value for the null hypothesis: mean(a) - mean(b) >= delta.
+ :param a: numpy 2d array
+ :param b: numpy 2d array
+ :param axis: which axis to do the calculations across
+ :param delta: test value of mean differences
+ :param left: if true then use <= delta instead of >= delta
+ :return: p-value
+ """
+ # implement our own pvalue calculation because the built-in t-test (t,p values)
+ # only offer delta=0 , e.g. m1-m1 ? 0
+ # we are however interested in m1-m2 ? delta
+ t_value = stats_tvalue(a, b, axis, delta)
+
+ # 2-sample degrees of freedom is using the array sizes - 2.
+ dof = a.shape[axis] + b.shape[axis] - 2
+
+ if left:
+ # left tailed test. e.g. m1-m2 <= delta
+ return sc.t.cdf(t_value, dof)
+ else:
+ # right tailed test. e.g. m1-m2 >= delta
+ return sc.t.sf(t_value, dof)
+ # a left+right tailed test is a 2-tail t-test and can be done using ttest_ind for delta=0
+
+def print_comparable_analysis(comparable_metrics_iter, label_header, data_header, output_comparable: str, output_comparable_significant: str):
+ baseline_value = _BASELINE[1]
+ baseline_index = label_header.index(_BASELINE[0])
+
+ old_baseline_label_list = None
+ delta = _DELTA
+ filter_value = _IGNORE_PAIR[1]
+ filter_index = label_header.index(_IGNORE_PAIR[0])
+
+ pvalue_threshold = _PVALUE_THRESHOLD
+ ci_threshold = (1 - _PVALUE_THRESHOLD) * 100.0
+
+ with open(output_comparable, "w") as output_file:
+
+ csv_writer = csv.writer(output_file)
+ csv_writer.writerow(label_header + ['mean', 'mean_diff', 'sem', 'pvalue_2tailed', 'pvalue_gt%d' %(_DELTA), 'pvalue_gt%d' %(_DELTA2)])
+
+ print("------------------------------------------------------------------")
+ print("Comparison against the baseline %s = %s" %(_BASELINE, baseline_value))
+ print("--- Right-tailed t-test checks if the baseline >= current %s by at least %d" %(_BASELINE[0], delta))
+ print()
+
+ global_stats = {'better_than_delta': [], 'better_than_delta_p95': []}
+
+ for nested_it in comparable_metrics_iter:
+ print("************************")
+
+ better_than_delta = []
+ better_than_delta_p95 = []
+
+ saw_baseline_once = False
+
+ for ((baseline_label_list, baseline_np_data_2d), (rest_label_list, rest_np_data_2d)) in nested_it:
+ _debug_print("baseline_label_list:", baseline_label_list)
+ _debug_print("baseline_np_data_2d:", baseline_np_data_2d)
+ _debug_print("rest_label_list:", rest_label_list)
+ _debug_print("rest_np_data_2d:", rest_np_data_2d)
+
+ mean_diff = baseline_np_data_2d.mean(axis=0) - rest_np_data_2d.mean(axis=0)
+ # 2-sample 2-tailed t-test with delta=0
+ # e.g. "Is it true that usually the two sample means are different?"
+ t_statistic, t_pvalue = sc.ttest_ind(baseline_np_data_2d, rest_np_data_2d, axis=0)
+
+ # 2-sample 1-tailed t-test with delta=50
+ # e.g. "Is it true that usually the sample means better than 50ms?"
+ t2 = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta)
+ p2 = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=delta)
+
+ t2_b = stats_tvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2)
+ p2_b = stats_pvalue(baseline_np_data_2d, rest_np_data_2d, axis=0, delta=_DELTA2)
+
+ print("%s vs %s" %(rest_label_list, baseline_value))
+ print(" ", data_header)
+ print("Mean Difference: ", mean_diff)
+ print("T-test (2-tailed) != 0: t=%s, p=%s" %(t_statistic, t_pvalue))
+ print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA, t2, p2))
+ print("T-test (right-tailed) >= %d: t=%s, p=%s" %(_DELTA2, t2_b, p2_b))
+
+ def write_out_values(label_list, *args):
+ csv_writer.writerow(label_list + [i[_PLOT_DATA_INDEX] for i in args])
+
+ sem = stats_standard_error(baseline_np_data_2d, rest_np_data_2d, axis=0)
+ if saw_baseline_once == False:
+ saw_baseline_once = True
+ base_sem = stats_standard_error_one(baseline_np_data_2d, axis=0)
+ write_out_values(baseline_label_list, baseline_np_data_2d.mean(axis=0), [0], base_sem, [None], [None], [None])
+ write_out_values(rest_label_list, rest_np_data_2d.mean(axis=0), mean_diff, sem, t_pvalue, p2, p2_b)
+
+ # now do the global statistics aggregation
+
+ if rest_label_list[filter_index] == filter_value:
+ continue
+
+ if mean_diff > delta:
+ better_than_delta.append((mean_diff, p2, rest_label_list))
+
+ if p2 <= pvalue_threshold:
+ better_than_delta_p95.append((mean_diff, rest_label_list))
+
+ if better_than_delta:
+ global_stats['better_than_delta'].append(better_than_delta)
+ if better_than_delta_p95:
+ global_stats['better_than_delta_p95'].append(better_than_delta_p95)
+
+ print("------------------------")
+ print("Global statistics:")
+ print("//// Rows with %s=%s are ignored here." %_IGNORE_PAIR)
+ print("- # of results with mean diff better than delta(%d) = %d" %(delta, len(global_stats['better_than_delta'])))
+ print(" > (meandiff, pvalue, labels)")
+ for i in global_stats['better_than_delta']:
+ print(" > %s" %i)
+ print("- # of results with mean diff better than delta(%d) CI%d%% = %d" %(delta, ci_threshold, len(global_stats['better_than_delta_p95'])))
+ print(" > (meandiff, labels)")
+ for i in global_stats['better_than_delta_p95']:
+ print(" > %s" %i)
+
+def main():
+ global _debug
+ global _DELTA
+ global _PVALUE_THRESHOLD
+
+ opts = parse_options()
+ _debug = opts.debug
+ _debug_print("parsed options: ", opts)
+
+ _PVALUE_THRESHOLD = opts.pvalue_threshold or _PVALUE_THRESHOLD
+
+ for file_name in opts.input_files:
+ with open(file_name, 'r') as input_file:
+ (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file)
+ print_analysis(grouped_numpy_iter, label_header, data_header, opts.output_samples)
+
+ with open(file_name, 'r') as input_file:
+ (grouped_numpy_iter, label_header, data_header) = from_file_group_by_labels(input_file)
+ without_baseline_iter = group_by_without_baseline_key(grouped_numpy_iter, label_header)
+ #_debug_print_gen(without_baseline_iter)
+
+ comparable_metrics_iter = iterate_comparable_metrics(without_baseline_iter, label_header)
+ print_comparable_analysis(comparable_metrics_iter, label_header, data_header, opts.output_comparable, opts.output_comparable_significant)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
new file mode 100755
index 0000000..25ee6f7
--- /dev/null
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -0,0 +1,393 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+
+#
+#
+# Measure application start-up time by launching applications under various combinations.
+# See --help for more details.
+#
+#
+# Sample usage:
+# $> ./app_startup_runner.py -p com.google.android.calculator -r warm -r cold -lc 10 -o out.csv
+# $> ./analyze_metrics.py out.csv
+#
+#
+
+import argparse
+import csv
+import itertools
+import os
+import sys
+import tempfile
+from datetime import timedelta
+from typing import Any, Callable, Iterable, List, NamedTuple, TextIO, Tuple, \
+ TypeVar, Union, Optional
+
+# local import
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+import lib.cmd_utils as cmd_utils
+import lib.print_utils as print_utils
+from app_startup.run_app_with_prefetch import PrefetchAppRunner
+import app_startup.lib.args_utils as args_utils
+from app_startup.lib.data_frame import DataFrame
+from app_startup.lib.perfetto_trace_collector import PerfettoTraceCollector
+from iorap.compiler import CompilerType
+import iorap.compiler as compiler
+
+# The following command line options participate in the combinatorial generation.
+# All other arguments have a global effect.
+_COMBINATORIAL_OPTIONS = ['package', 'readahead', 'compiler_filter',
+ 'activity', 'trace_duration']
+_TRACING_READAHEADS = ['mlock', 'fadvise']
+_FORWARD_OPTIONS = {'loop_count': '--count'}
+_RUN_SCRIPT = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ 'run_app_with_prefetch.py')
+
+CollectorPackageInfo = NamedTuple('CollectorPackageInfo',
+ [('package', str), ('compiler_filter', str)])
+# by 2; systrace starts up slowly.
+
+_UNLOCK_SCREEN_SCRIPT = os.path.join(
+ os.path.dirname(os.path.realpath(__file__)), 'unlock_screen')
+
+RunCommandArgs = NamedTuple('RunCommandArgs',
+ [('package', str),
+ ('readahead', str),
+ ('activity', Optional[str]),
+ ('compiler_filter', Optional[str]),
+ ('timeout', Optional[int]),
+ ('debug', bool),
+ ('simulate', bool),
+ ('input', Optional[str]),
+ ('trace_duration', Optional[timedelta])])
+
+# This must be the only mutable global variable. All other global variables are constants to avoid magic literals.
+_debug = False # See -d/--debug flag.
+_DEBUG_FORCE = None # Ignore -d/--debug if this is not none.
+_PERFETTO_TRACE_DURATION_MS = 5000 # milliseconds
+_PERFETTO_TRACE_DURATION = timedelta(milliseconds=_PERFETTO_TRACE_DURATION_MS)
+
+# Type hinting names.
+T = TypeVar('T')
+NamedTupleMeta = Callable[
+ ..., T] # approximation of a (S : NamedTuple<T> where S() == T) metatype.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Run one or more Android "
+ "applications under various "
+ "settings in order to measure "
+ "startup time.")
+ # argparse considers args starting with - and -- optional in --help, even though required=True.
+ # by using a named argument group --help will clearly say that it's required instead of optional.
+ required_named = parser.add_argument_group('required named arguments')
+ required_named.add_argument('-p', '--package', action='append',
+ dest='packages',
+ help='package of the application', required=True)
+ required_named.add_argument('-r', '--readahead', action='append',
+ dest='readaheads',
+ help='which readahead mode to use',
+ choices=('warm', 'cold', 'mlock', 'fadvise'),
+ required=True)
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-c', '--compiler-filter', action='append',
+ dest='compiler_filters',
+ help='which compiler filter to use. if omitted it does not enforce the app\'s compiler filter',
+ choices=('speed', 'speed-profile', 'quicken'))
+ optional_named.add_argument('-s', '--simulate', dest='simulate',
+ action='store_true',
+ help='Print which commands will run, but don\'t run the apps')
+ optional_named.add_argument('-d', '--debug', dest='debug',
+ action='store_true',
+ help='Add extra debugging output')
+ optional_named.add_argument('-o', '--output', dest='output', action='store',
+ help='Write CSV output to file.')
+ optional_named.add_argument('-t', '--timeout', dest='timeout', action='store',
+ type=int, default=10,
+ help='Timeout after this many seconds when executing a single run.')
+ optional_named.add_argument('-lc', '--loop-count', dest='loop_count',
+ default=1, type=int, action='store',
+ help='How many times to loop a single run.')
+ optional_named.add_argument('-in', '--inodes', dest='inodes', type=str,
+ action='store',
+ help='Path to inodes file (system/extras/pagecache/pagecache.py -d inodes)')
+ optional_named.add_argument('--compiler-trace-duration-ms',
+ dest='trace_duration',
+ type=lambda ms_str: timedelta(milliseconds=int(ms_str)),
+ action='append',
+ help='The trace duration (milliseconds) in '
+ 'compilation')
+ optional_named.add_argument('--compiler-type', dest='compiler_type',
+ type=CompilerType, choices=list(CompilerType),
+ default=CompilerType.DEVICE,
+ help='The type of compiler.')
+
+ return parser.parse_args(argv)
+
+def key_to_cmdline_flag(key: str) -> str:
+ """Convert key into a command line flag, e.g. 'foo-bars' -> '--foo-bar' """
+ if key.endswith("s"):
+ key = key[:-1]
+ return "--" + key.replace("_", "-")
+
+def as_run_command(tpl: NamedTuple) -> List[Union[str, Any]]:
+ """
+ Convert a named tuple into a command-line compatible arguments list.
+
+ Example: ABC(1, 2, 3) -> ['--a', 1, '--b', 2, '--c', 3]
+ """
+ args = []
+ for key, value in tpl._asdict().items():
+ if value is None:
+ continue
+ args.append(key_to_cmdline_flag(key))
+ args.append(value)
+ return args
+
+def run_perfetto_collector(collector_info: CollectorPackageInfo,
+ timeout: int,
+ simulate: bool) -> Tuple[bool, TextIO]:
+ """Run collector to collect prefetching trace.
+
+ Returns:
+ A tuple of whether the collection succeeds and the generated trace file.
+ """
+ tmp_output_file = tempfile.NamedTemporaryFile()
+
+ collector = PerfettoTraceCollector(package=collector_info.package,
+ activity=None,
+ compiler_filter=collector_info.compiler_filter,
+ timeout=timeout,
+ simulate=simulate,
+ trace_duration=_PERFETTO_TRACE_DURATION,
+ save_destination_file_path=tmp_output_file.name)
+ result = collector.run()
+
+ return result is not None, tmp_output_file
+
+def parse_run_script_csv_file(csv_file: TextIO) -> DataFrame:
+ """Parse a CSV file full of integers into a DataFrame."""
+ csv_reader = csv.reader(csv_file)
+
+ try:
+ header_list = next(csv_reader)
+ except StopIteration:
+ header_list = []
+
+ if not header_list:
+ return None
+
+ headers = [i for i in header_list]
+
+ d = {}
+ for row in csv_reader:
+ header_idx = 0
+
+ for i in row:
+ v = i
+ if i:
+ v = int(i)
+
+ header_key = headers[header_idx]
+ l = d.get(header_key, [])
+ l.append(v)
+ d[header_key] = l
+
+ header_idx = header_idx + 1
+
+ return DataFrame(d)
+
+def build_ri_compiler_argv(inodes_path: str,
+ perfetto_trace_file: str,
+ trace_duration: Optional[timedelta]
+ ) -> str:
+ argv = ['-i', inodes_path, '--perfetto-trace',
+ perfetto_trace_file]
+
+ if trace_duration is not None:
+ argv += ['--duration', str(int(trace_duration.total_seconds()
+ * PerfettoTraceCollector.MS_PER_SEC))]
+
+ print_utils.debug_print(argv)
+ return argv
+
+def execute_run_using_perfetto_trace(collector_info,
+ run_combos: Iterable[RunCommandArgs],
+ simulate: bool,
+ inodes_path: str,
+ timeout: int,
+ compiler_type: CompilerType,
+ requires_trace_collection: bool) -> DataFrame:
+ """ Executes run based on perfetto trace. """
+ if requires_trace_collection:
+ passed, perfetto_trace_file = run_perfetto_collector(collector_info,
+ timeout,
+ simulate)
+ if not passed:
+ raise RuntimeError('Cannot run perfetto collector!')
+ else:
+ perfetto_trace_file = tempfile.NamedTemporaryFile()
+
+ with perfetto_trace_file:
+ for combos in run_combos:
+ if combos.readahead in _TRACING_READAHEADS:
+ if simulate:
+ compiler_trace_file = tempfile.NamedTemporaryFile()
+ else:
+ ri_compiler_argv = build_ri_compiler_argv(inodes_path,
+ perfetto_trace_file.name,
+ combos.trace_duration)
+ compiler_trace_file = compiler.compile(compiler_type,
+ inodes_path,
+ ri_compiler_argv,
+ combos.package,
+ combos.activity)
+
+ with compiler_trace_file:
+ combos = combos._replace(input=compiler_trace_file.name)
+ print_utils.debug_print(combos)
+ output = PrefetchAppRunner(**combos._asdict()).run()
+ else:
+ print_utils.debug_print(combos)
+ output = PrefetchAppRunner(**combos._asdict()).run()
+
+ yield DataFrame(dict((x, [y]) for x, y in output)) if output else None
+
+def execute_run_combos(
+ grouped_run_combos: Iterable[Tuple[CollectorPackageInfo, Iterable[RunCommandArgs]]],
+ simulate: bool,
+ inodes_path: str,
+ timeout: int,
+ compiler_type: CompilerType,
+ requires_trace_collection: bool):
+ # nothing will work if the screen isn't unlocked first.
+ cmd_utils.execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT],
+ timeout,
+ simulate=simulate,
+ shell=False)
+
+ for collector_info, run_combos in grouped_run_combos:
+ yield from execute_run_using_perfetto_trace(collector_info,
+ run_combos,
+ simulate,
+ inodes_path,
+ timeout,
+ compiler_type,
+ requires_trace_collection)
+
+def gather_results(commands: Iterable[Tuple[DataFrame]],
+ key_list: List[str], value_list: List[Tuple[str, ...]]):
+ print_utils.debug_print("gather_results: key_list = ", key_list)
+ stringify_none = lambda s: s is None and "<none>" or s
+ # yield key_list + ["time(ms)"]
+ for (run_result_list, values) in itertools.zip_longest(commands, value_list):
+ print_utils.debug_print("run_result_list = ", run_result_list)
+ print_utils.debug_print("values = ", values)
+
+ if not run_result_list:
+ continue
+
+ # RunCommandArgs(package='com.whatever', readahead='warm', compiler_filter=None)
+ # -> {'package':['com.whatever'], 'readahead':['warm'], 'compiler_filter':[None]}
+ values_dict = {}
+ for k, v in values._asdict().items():
+ if not k in key_list:
+ continue
+ values_dict[k] = [stringify_none(v)]
+
+ values_df = DataFrame(values_dict)
+ # project 'values_df' to be same number of rows as run_result_list.
+ values_df = values_df.repeat(run_result_list.data_row_len)
+
+ # the results are added as right-hand-side columns onto the existing labels for the table.
+ values_df.merge_data_columns(run_result_list)
+
+ yield values_df
+
+def eval_and_save_to_csv(output, annotated_result_values):
+ printed_header = False
+
+ csv_writer = csv.writer(output)
+ for row in annotated_result_values:
+ if not printed_header:
+ headers = row.headers
+ csv_writer.writerow(headers)
+ printed_header = True
+ # TODO: what about when headers change?
+
+ for data_row in row.data_table:
+ data_row = [d for d in data_row]
+ csv_writer.writerow(data_row)
+
+ output.flush() # see the output live.
+
+def coerce_to_list(opts: dict):
+ """Tranform values of the dictionary to list.
+ For example:
+ 1 -> [1], None -> [None], [1,2,3] -> [1,2,3]
+ [[1],[2]] -> [[1],[2]], {1:1, 2:2} -> [{1:1, 2:2}]
+ """
+ result = {}
+ for key in opts:
+ val = opts[key]
+ result[key] = val if issubclass(type(val), list) else [val]
+ return result
+
+def main():
+ global _debug
+
+ opts = parse_options()
+ _debug = opts.debug
+ if _DEBUG_FORCE is not None:
+ _debug = _DEBUG_FORCE
+
+ print_utils.DEBUG = _debug
+ cmd_utils.SIMULATE = opts.simulate
+
+ print_utils.debug_print("parsed options: ", opts)
+
+ output_file = opts.output and open(opts.output, 'w') or sys.stdout
+
+ combos = lambda: args_utils.generate_run_combinations(
+ RunCommandArgs,
+ coerce_to_list(vars(opts)),
+ opts.loop_count)
+ print_utils.debug_print_gen("run combinations: ", combos())
+
+ grouped_combos = lambda: args_utils.generate_group_run_combinations(combos(),
+ CollectorPackageInfo)
+
+ print_utils.debug_print_gen("grouped run combinations: ", grouped_combos())
+ requires_trace_collection = any(i in _TRACING_READAHEADS for i in opts.readaheads)
+ exec = execute_run_combos(grouped_combos(),
+ opts.simulate,
+ opts.inodes,
+ opts.timeout,
+ opts.compiler_type,
+ requires_trace_collection)
+
+ results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos())
+
+ eval_and_save_to_csv(output_file, results)
+
+ return 1
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py
new file mode 100755
index 0000000..0c2bbea
--- /dev/null
+++ b/startop/scripts/app_startup/app_startup_runner_test.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+#
+
+"""
+Unit tests for the app_startup_runner.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./app_startup_runner_test.py
+ $> pytest app_startup_runner_test.py
+ $> python -m pytest app_startup_runner_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+import io
+import shlex
+import sys
+import typing
+# global imports
+from contextlib import contextmanager
+
+# local imports
+import app_startup_runner as asr
+# pip imports
+import pytest
+
+#
+# Argument Parsing Helpers
+#
+
+@contextmanager
+def ignore_stdout_stderr():
+ """Ignore stdout/stderr output for duration of this context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ sys.stdout = io.StringIO()
+ sys.stderr = io.StringIO()
+ try:
+ yield
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+
+@contextmanager
+def argparse_bad_argument(msg):
+ """
+ Assert that a SystemExit is raised when executing this context.
+ If the assertion fails, print the message 'msg'.
+ """
+ with pytest.raises(SystemExit, message=msg):
+ with ignore_stdout_stderr():
+ yield
+
+def assert_bad_argument(args, msg):
+ """
+ Assert that the command line arguments in 'args' are malformed.
+ Prints 'msg' if the assertion fails.
+ """
+ with argparse_bad_argument(msg):
+ parse_args(args)
+
+def parse_args(args):
+ """
+ :param args: command-line like arguments as a single string
+ :return: dictionary of parsed key/values
+ """
+ # "-a b -c d" => ['-a', 'b', '-c', 'd']
+ return vars(asr.parse_options(shlex.split(args)))
+
+def default_dict_for_parsed_args(**kwargs):
+ """
+ # Combine it with all of the "optional" parameters' default values.
+ """
+ d = {'compiler_filters': None, 'simulate': False, 'debug': False,
+ 'output': None, 'timeout': 10, 'loop_count': 1, 'inodes': None,
+ 'trace_duration': None, 'compiler_type': asr.CompilerType.DEVICE}
+ d.update(kwargs)
+ return d
+
+def default_mock_dict_for_parsed_args(include_optional=True, **kwargs):
+ """
+ Combine default dict with all optional parameters with some mock required parameters.
+ """
+ d = {'packages': ['com.fake.package'], 'readaheads': ['warm']}
+ if include_optional:
+ d.update(default_dict_for_parsed_args())
+ d.update(kwargs)
+ return d
+
+def parse_optional_args(str):
+ """
+ Parse an argument string which already includes all the required arguments
+ in default_mock_dict_for_parsed_args.
+ """
+ req = "--package com.fake.package --readahead warm"
+ return parse_args("%s %s" % (req, str))
+
+def test_argparse():
+ # missing arguments
+ assert_bad_argument("", "-p and -r are required")
+ assert_bad_argument("-r warm", "-p is required")
+ assert_bad_argument("--readahead warm", "-p is required")
+ assert_bad_argument("-p com.fake.package", "-r is required")
+ assert_bad_argument("--package com.fake.package", "-r is required")
+
+ # required arguments are parsed correctly
+ ad = default_dict_for_parsed_args # assert dict
+
+ assert parse_args("--package xyz --readahead warm") == ad(packages=['xyz'],
+ readaheads=['warm'])
+ assert parse_args("-p xyz -r warm") == ad(packages=['xyz'],
+ readaheads=['warm'])
+
+ assert parse_args("-p xyz -r warm -s") == ad(packages=['xyz'],
+ readaheads=['warm'],
+ simulate=True)
+ assert parse_args("-p xyz -r warm --simulate") == ad(packages=['xyz'],
+ readaheads=['warm'],
+ simulate=True)
+
+ # optional arguments are parsed correctly.
+ mad = default_mock_dict_for_parsed_args # mock assert dict
+ assert parse_optional_args("--output filename.csv") == mad(
+ output='filename.csv')
+ assert parse_optional_args("-o filename.csv") == mad(output='filename.csv')
+
+ assert parse_optional_args("--timeout 123") == mad(timeout=123)
+ assert parse_optional_args("-t 456") == mad(timeout=456)
+
+ assert parse_optional_args("--loop-count 123") == mad(loop_count=123)
+ assert parse_optional_args("-lc 456") == mad(loop_count=456)
+
+ assert parse_optional_args("--inodes bar") == mad(inodes="bar")
+ assert parse_optional_args("-in baz") == mad(inodes="baz")
+
+
+
+def test_key_to_cmdline_flag():
+ assert asr.key_to_cmdline_flag("abc") == "--abc"
+ assert asr.key_to_cmdline_flag("foos") == "--foo"
+ assert asr.key_to_cmdline_flag("ba_r") == "--ba-r"
+ assert asr.key_to_cmdline_flag("ba_zs") == "--ba-z"
+
+def test_parse_run_script_csv_file():
+ # empty file -> empty list
+ f = io.StringIO("")
+ assert asr.parse_run_script_csv_file(f) == None
+
+ # common case
+ f = io.StringIO("TotalTime_ms,Displayed_ms\n1,2")
+ df = asr.DataFrame({'TotalTime_ms': [1], 'Displayed_ms': [2]})
+
+ pf = asr.parse_run_script_csv_file(f)
+ assert pf == df
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/app_startup/force_compiler_filter b/startop/scripts/app_startup/force_compiler_filter
new file mode 100755
index 0000000..08f983d
--- /dev/null
+++ b/startop/scripts/app_startup/force_compiler_filter
@@ -0,0 +1,143 @@
+#!/bin/bash
+#
+# Copyright 2018, 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.
+
+#
+# Forces an application APK to be compiled (by ART's dex2oat)
+# with a specific compiler filter.
+#
+# Example usage:
+# $> ./force_compiler_filter -p com.google.android.apps.maps -c speed-profile
+#
+# (The application may be started/stopped as a side effect)
+#
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+usage() {
+ cat <<EOF
+Usage: $(basename $0) [OPTION]...
+
+ Required:
+ -p, --package package of the app to recompile
+ -c, --compiler-filter override the compiler filter if set (default none)
+ valid options are listed by: adb shell cmd package, under compile -m
+
+ Optional:
+ -a, --activity activity of the app to recompile
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -w, --wait_time how long to wait for app startup (default 10) in seconds
+EOF
+}
+
+wait_time="10" # seconds
+
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -w|--wait_time)
+ wait_time="$2"
+ shift
+ ;;
+ -c|--compiler-filter)
+ compiler_filter="$2"
+ shift
+ ;;
+ -v|--verbose)
+ verbose="y"
+ ;;
+ esac
+ shift
+ done
+
+ if [[ -z "$compiler_filter" ]]; then
+ echo "Missing required --compiler-filter" >&2
+ echo ""
+ usage
+ exit 1
+ fi
+ if [[ -z "$package" ]]; then
+ echo "Missing required --package" >&2
+ echo ""
+ usage
+ exit 1
+ fi
+
+ if [[ "$activity" == "" ]]; then
+ activity="$(get_activity_name "$package")"
+ if [[ "$activity" == "" ]]; then
+ echo "Activity name could not be found, invalid package name?" 1>&2
+ exit 1
+ else
+ verbose_print "Activity name inferred: " "$activity"
+ fi
+ fi
+}
+
+force_package_compilation() {
+ local arg_compiler_filter="$1"
+ local arg_package="$2"
+
+ if [[ $arg_compiler_filter == speed-profile ]]; then
+ # Force the running app to dump its profiles to disk.
+ remote_pkill "$arg_package" -SIGUSR1
+ sleep 1 # give some time for above to complete.
+ fi
+
+ adb shell cmd package compile -m "$arg_compiler_filter" -f "$arg_package"
+}
+
+main() {
+ parse_arguments "$@"
+
+ if [[ $compiler_filter == speed-profile ]]; then
+ # screen needs to be unlocked in order to run an app
+ "$DIR"/unlock_screen
+
+ local output=$("$DIR"/launch_application "$package" "$activity")
+ if [[ $? -ne 0 ]]; then
+ echo "launching application failed" >&2
+ exit 1
+ fi
+
+ verbose_print "$output"
+ # give some time for app startup to complete.
+ # this is supposed to be an upper bound for measuring startup time.
+ sleep "$wait_time"
+ fi
+
+ force_package_compilation "$compiler_filter" "$package"
+
+ # kill the application to ensure next time it's started,
+ # it picks up the correct compilation filter.
+ adb shell am force-stop "$package"
+ remote_pkill "$package"
+}
+
+main "$@"
diff --git a/startop/scripts/app_startup/launch_application b/startop/scripts/app_startup/launch_application
new file mode 100755
index 0000000..6704a5a
--- /dev/null
+++ b/startop/scripts/app_startup/launch_application
@@ -0,0 +1,52 @@
+#!/bin/bash
+#
+# Copyright 2018, 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+launch_application_usage() {
+ cat <<EOF
+Usage: $(basename $0) <package> <activity>
+
+ Positional Arguments:
+ <package> package of the app to test
+ <activity> activity to use
+
+ Named Arguments:
+ -h, --help usage information (this)
+EOF
+}
+
+launch_application() {
+ local package="$1"
+ local activity="$2"
+
+ # if there's any $s inside of the activity name, it needs to be escaped to \$.
+ # example '.app.honeycomb.Shell$HomeActivity'
+ # if the $ is not escaped, adb shell will try to evaluate $HomeActivity to a variable.
+ activity=${activity//\$/\\$}
+
+ adb shell am start -S -W "$package"/"$activity"
+
+ # pipe this into 'parse_metrics' to parse the output.
+}
+
+if [[ $# -lt 2 ]]; then
+ launch_application_usage
+ exit 1
+fi
+
+launch_application "$@"
diff --git a/startop/scripts/app_startup/lib/adb_utils.py b/startop/scripts/app_startup/lib/adb_utils.py
new file mode 100644
index 0000000..3cebc9a
--- /dev/null
+++ b/startop/scripts/app_startup/lib/adb_utils.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Helper util libraries for calling adb command line."""
+
+import datetime
+import os
+import re
+import sys
+import time
+from typing import Optional
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__)))))
+import lib.cmd_utils as cmd_utils
+import lib.logcat_utils as logcat_utils
+
+
+def logcat_save_timestamp() -> str:
+ """Gets the current logcat timestamp.
+
+ Returns:
+ A string of timestamp.
+ """
+ _, output = cmd_utils.run_adb_shell_command(
+ "date -u +\'%Y-%m-%d %H:%M:%S.%N\'")
+ return output
+
+def vm_drop_cache():
+ """Free pagecache and slab object."""
+ cmd_utils.run_adb_shell_command('echo 3 > /proc/sys/vm/drop_caches')
+ # Sleep a little bit to provide enough time for cache cleanup.
+ time.sleep(1)
+
+def root():
+ """Roots adb and successive adb commands will run under root."""
+ cmd_utils.run_shell_command('adb root')
+
+def disable_selinux():
+ """Disables selinux setting."""
+ _, output = cmd_utils.run_adb_shell_command('getenforce')
+ if output == 'Permissive':
+ return
+
+ print('Disable selinux permissions and restart framework.')
+ cmd_utils.run_adb_shell_command('setenforce 0')
+ cmd_utils.run_adb_shell_command('stop')
+ cmd_utils.run_adb_shell_command('start')
+ cmd_utils.run_shell_command('adb wait-for-device')
+
+def pkill(procname: str):
+ """Kills a process on device specified by the substring pattern in procname"""
+ _, pids = cmd_utils.run_shell_command('adb shell ps | grep "{}" | '
+ 'awk \'{{print $2;}}\''.
+ format(procname))
+
+ for pid in pids.split('\n'):
+ pid = pid.strip()
+ if pid:
+ passed,_ = cmd_utils.run_adb_shell_command('kill {}'.format(pid))
+ time.sleep(1)
+
+def parse_time_to_milliseconds(time: str) -> int:
+ """Parses the time string to milliseconds."""
+ # Example: +1s56ms, +56ms
+ regex = r'\+((?P<second>\d+?)s)?(?P<millisecond>\d+?)ms'
+ result = re.search(regex, time)
+ second = 0
+ if result.group('second'):
+ second = int(result.group('second'))
+ ms = int(result.group('millisecond'))
+ return second * 1000 + ms
+
+def blocking_wait_for_logcat_displayed_time(timestamp: datetime.datetime,
+ package: str,
+ timeout: int) -> Optional[int]:
+ """Parses the displayed time in the logcat.
+
+ Returns:
+ the displayed time.
+ """
+ pattern = re.compile('.*ActivityTaskManager: Displayed {}.*'.format(package))
+ # 2019-07-02 22:28:34.469453349 -> 2019-07-02 22:28:34.469453
+ timestamp = datetime.datetime.strptime(timestamp[:-3],
+ '%Y-%m-%d %H:%M:%S.%f')
+ timeout_dt = timestamp + datetime.timedelta(0, timeout)
+ # 2019-07-01 14:54:21.946 27365 27392 I ActivityTaskManager:
+ # Displayed com.android.settings/.Settings: +927ms
+ result = logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
+ pattern,
+ timeout_dt)
+ if not result or not '+' in result:
+ return None
+ displayed_time = result[result.rfind('+'):]
+
+ return parse_time_to_milliseconds(displayed_time)
+
+def delete_file_on_device(file_path: str) -> None:
+ """ Deletes a file on the device. """
+ cmd_utils.run_adb_shell_command(
+ "[[ -f '{file_path}' ]] && rm -f '{file_path}' || "
+ "exit 0".format(file_path=file_path))
+
+def set_prop(property: str, value: str) -> None:
+ """ Sets property using adb shell. """
+ cmd_utils.run_adb_shell_command('setprop "{property}" "{value}"'.format(
+ property=property, value=value))
+
+def pull_file(device_file_path: str, output_file_path: str) -> None:
+ """ Pulls file from device to output """
+ cmd_utils.run_shell_command('adb pull "{device_file_path}" "{output_file_path}"'.
+ format(device_file_path=device_file_path,
+ output_file_path=output_file_path))
diff --git a/startop/scripts/app_startup/lib/adb_utils_test.py b/startop/scripts/app_startup/lib/adb_utils_test.py
new file mode 100644
index 0000000..e590fed
--- /dev/null
+++ b/startop/scripts/app_startup/lib/adb_utils_test.py
@@ -0,0 +1,16 @@
+import adb_utils
+
+# pip imports
+import pytest
+
+def test_parse_time_to_milliseconds():
+ # Act
+ result1 = adb_utils.parse_time_to_milliseconds('+1s7ms')
+ result2 = adb_utils.parse_time_to_milliseconds('+523ms')
+
+ # Assert
+ assert result1 == 1007
+ assert result2 == 523
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/app_startup/lib/app_runner.py b/startop/scripts/app_startup/lib/app_runner.py
new file mode 100644
index 0000000..78873fa
--- /dev/null
+++ b/startop/scripts/app_startup/lib/app_runner.py
@@ -0,0 +1,266 @@
+# Copyright 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.
+
+"""Class to run an app."""
+import os
+import sys
+from typing import Optional, List, Tuple
+
+# local import
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__)))))
+
+import app_startup.lib.adb_utils as adb_utils
+import lib.cmd_utils as cmd_utils
+import lib.print_utils as print_utils
+
+class AppRunnerListener(object):
+ """Interface for lisenter of AppRunner. """
+
+ def preprocess(self) -> None:
+ """Preprocess callback to initialized before the app is running. """
+ pass
+
+ def postprocess(self, pre_launch_timestamp: str) -> None:
+ """Postprocess callback to cleanup after the app is running.
+
+ param:
+ 'pre_launch_timestamp': indicates the timestamp when the app is
+ launching.. """
+ pass
+
+ def metrics_selector(self, am_start_output: str,
+ pre_launch_timestamp: str) -> None:
+ """A metrics selection callback that waits for the desired metrics to
+ show up in logcat.
+ params:
+ 'am_start_output': indicates the output of app startup.
+ 'pre_launch_timestamp': indicates the timestamp when the app is
+ launching.
+ returns:
+ a string in the format of "<metric>=<value>\n<metric>=<value>\n..."
+ for further parsing. For example "TotalTime=123\nDisplayedTime=121".
+ Return an empty string if no metrics need to be parsed further.
+ """
+ pass
+
+class AppRunner(object):
+ """ Class to run an app. """
+ # static variables
+ DIR = os.path.abspath(os.path.dirname(__file__))
+ APP_STARTUP_DIR = os.path.dirname(DIR)
+ IORAP_COMMON_BASH_SCRIPT = os.path.realpath(os.path.join(DIR,
+ '../../iorap/common'))
+ DEFAULT_TIMEOUT = 30 # seconds
+
+ def __init__(self,
+ package: str,
+ activity: Optional[str],
+ compiler_filter: Optional[str],
+ timeout: Optional[int],
+ simulate: bool):
+ self.package = package
+ self.simulate = simulate
+
+ # If the argument activity is None, try to set it.
+ self.activity = activity
+ if self.simulate:
+ self.activity = 'act'
+ if self.activity is None:
+ self.activity = AppRunner.get_activity(self.package)
+
+ self.compiler_filter = compiler_filter
+ self.timeout = timeout if timeout else AppRunner.DEFAULT_TIMEOUT
+
+ self.listeners = []
+
+ def add_callbacks(self, listener: AppRunnerListener):
+ self.listeners.append(listener)
+
+ def remove_callbacks(self, listener: AppRunnerListener):
+ self.listeners.remove(listener)
+
+ @staticmethod
+ def get_activity(package: str) -> str:
+ """ Tries to set the activity based on the package. """
+ passed, activity = cmd_utils.run_shell_func(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT,
+ 'get_activity_name',
+ [package])
+
+ if not passed or not activity:
+ raise ValueError(
+ 'Activity name could not be found, invalid package name?!')
+
+ return activity
+
+ def configure_compiler_filter(self) -> bool:
+ """Configures compiler filter (e.g. speed).
+
+ Returns:
+ A bool indicates whether configure of compiler filer succeeds or not.
+ """
+ if not self.compiler_filter:
+ print_utils.debug_print('No --compiler-filter specified, don\'t'
+ ' need to force it.')
+ return True
+
+ passed, current_compiler_filter_info = \
+ cmd_utils.run_shell_command(
+ '{} --package {}'.format(os.path.join(AppRunner.APP_STARTUP_DIR,
+ 'query_compiler_filter.py'),
+ self.package))
+
+ if passed != 0:
+ return passed
+
+ # TODO: call query_compiler_filter directly as a python function instead of
+ # these shell calls.
+ current_compiler_filter, current_reason, current_isa = \
+ current_compiler_filter_info.split(' ')
+ print_utils.debug_print('Compiler Filter={} Reason={} Isa={}'.format(
+ current_compiler_filter, current_reason, current_isa))
+
+ # Don't trust reasons that aren't 'unknown' because that means
+ # we didn't manually force the compilation filter.
+ # (e.g. if any automatic system-triggered compilations are not unknown).
+ if current_reason != 'unknown' or \
+ current_compiler_filter != self.compiler_filter:
+ passed, _ = adb_utils.run_shell_command('{}/force_compiler_filter '
+ '--compiler-filter "{}" '
+ '--package "{}"'
+ ' --activity "{}'.
+ format(AppRunner.APP_STARTUP_DIR,
+ self.compiler_filter,
+ self.package,
+ self.activity))
+ else:
+ adb_utils.debug_print('Queried compiler-filter matched requested '
+ 'compiler-filter, skip forcing.')
+ passed = False
+ return passed
+
+ def run(self) -> Optional[List[Tuple[str]]]:
+ """Runs an app.
+
+ Returns:
+ A list of (metric, value) tuples.
+ """
+ print_utils.debug_print('==========================================')
+ print_utils.debug_print('===== START =====')
+ print_utils.debug_print('==========================================')
+ # Run the preprocess.
+ for listener in self.listeners:
+ listener.preprocess()
+
+ # Ensure the APK is currently compiled with whatever we passed in
+ # via --compiler-filter.
+ # No-op if this option was not passed in.
+ if not self.configure_compiler_filter():
+ print_utils.error_print('Compiler filter configuration failed!')
+ return None
+
+ pre_launch_timestamp = adb_utils.logcat_save_timestamp()
+ # Launch the app.
+ results = self.launch_app(pre_launch_timestamp)
+
+ # Run the postprocess.
+ for listener in self.listeners:
+ listener.postprocess(pre_launch_timestamp)
+
+ return results
+
+ def launch_app(self, pre_launch_timestamp: str) -> Optional[List[Tuple[str]]]:
+ """ Launches the app.
+
+ Returns:
+ A list of (metric, value) tuples.
+ """
+ print_utils.debug_print('Running with timeout {}'.format(self.timeout))
+
+ passed, am_start_output = cmd_utils.run_shell_command('timeout {timeout} '
+ '"{DIR}/launch_application" '
+ '"{package}" '
+ '"{activity}"'.
+ format(timeout=self.timeout,
+ DIR=AppRunner.APP_STARTUP_DIR,
+ package=self.package,
+ activity=self.activity))
+ if not passed and not self.simulate:
+ return None
+
+ return self.wait_for_app_finish(pre_launch_timestamp, am_start_output)
+
+ def wait_for_app_finish(self,
+ pre_launch_timestamp: str,
+ am_start_output: str) -> Optional[List[Tuple[str]]]:
+ """ Wait for app finish and all metrics are shown in logcat.
+
+ Returns:
+ A list of (metric, value) tuples.
+ """
+ if self.simulate:
+ return [('TotalTime', '123')]
+
+ ret = []
+ for listener in self.listeners:
+ output = listener.metrics_selector(am_start_output,
+ pre_launch_timestamp)
+ ret = ret + AppRunner.parse_metrics_output(output)
+
+ return ret
+
+ @staticmethod
+ def parse_metrics_output(input: str) -> List[
+ Tuple[str, str, str]]:
+ """Parses output of app startup to metrics and corresponding values.
+
+ It converts 'a=b\nc=d\ne=f\n...' into '[(a,b,''),(c,d,''),(e,f,'')]'
+
+ Returns:
+ A list of tuples that including metric name, metric value and rest info.
+ """
+ all_metrics = []
+ for line in input.split('\n'):
+ if not line:
+ continue
+ splits = line.split('=')
+ if len(splits) < 2:
+ print_utils.error_print('Bad line "{}"'.format(line))
+ continue
+ metric_name = splits[0]
+ metric_value = splits[1]
+ rest = splits[2] if len(splits) > 2 else ''
+ if rest:
+ print_utils.error_print('Corrupt line "{}"'.format(line))
+ print_utils.debug_print('metric: "{metric_name}", '
+ 'value: "{metric_value}" '.
+ format(metric_name=metric_name,
+ metric_value=metric_value))
+
+ all_metrics.append((metric_name, metric_value))
+ return all_metrics
+
+ @staticmethod
+ def parse_total_time( am_start_output: str) -> Optional[str]:
+ """Parses the total time from 'adb shell am start pkg' output.
+
+ Returns:
+ the total time of app startup.
+ """
+ for line in am_start_output.split('\n'):
+ if 'TotalTime:' in line:
+ return line[len('TotalTime:'):].strip()
+ return None
+
diff --git a/startop/scripts/app_startup/lib/app_runner_test.py b/startop/scripts/app_startup/lib/app_runner_test.py
new file mode 100644
index 0000000..33d233b
--- /dev/null
+++ b/startop/scripts/app_startup/lib/app_runner_test.py
@@ -0,0 +1,104 @@
+# Copyright 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.
+#
+
+"""Unit tests for the AppRunner."""
+import os
+import sys
+from pathlib import Path
+
+from app_runner import AppRunner, AppRunnerListener
+from mock import Mock, call, patch
+
+# The path is "frameworks/base/startop/scripts/"
+sys.path.append(Path(os.path.realpath(__file__)).parents[2])
+import lib.cmd_utils as cmd_utils
+
+class AppRunnerTestListener(AppRunnerListener):
+ def preprocess(self) -> None:
+ cmd_utils.run_shell_command('pre'),
+
+ def postprocess(self, pre_launch_timestamp: str) -> None:
+ cmd_utils.run_shell_command('post'),
+
+ def metrics_selector(self, am_start_output: str,
+ pre_launch_timestamp: str) -> None:
+ return 'TotalTime=123\n'
+
+RUNNER = AppRunner(package='music',
+ activity='MainActivity',
+ compiler_filter='speed',
+ timeout=None,
+ simulate=False)
+
+
+
+def test_configure_compiler_filter():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.return_value = (True, 'speed arm64 kUpToDate')
+
+ RUNNER.configure_compiler_filter()
+
+ calls = [call(os.path.realpath(
+ os.path.join(RUNNER.DIR,
+ '../query_compiler_filter.py')) + ' --package music')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_parse_metrics_output():
+ input = 'a1=b1\nc1=d1\ne1=f1'
+ ret = RUNNER.parse_metrics_output(input)
+
+ assert ret == [('a1', 'b1'), ('c1', 'd1'), ('e1', 'f1')]
+
+def _mocked_run_shell_command(*args, **kwargs):
+ if args[0] == 'adb shell "date -u +\'%Y-%m-%d %H:%M:%S.%N\'"':
+ return (True, "2019-07-02 23:20:06.972674825")
+ elif args[0] == 'adb shell ps | grep "music" | awk \'{print $2;}\'':
+ return (True, '9999')
+ else:
+ return (True, 'a1=b1\nc1=d1=d2\ne1=f1')
+
+@patch('app_startup.lib.adb_utils.blocking_wait_for_logcat_displayed_time')
+@patch('lib.cmd_utils.run_shell_command')
+def test_run(mock_run_shell_command,
+ mock_blocking_wait_for_logcat_displayed_time):
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ mock_blocking_wait_for_logcat_displayed_time.return_value = 123
+
+ test_listener = AppRunnerTestListener()
+ RUNNER.add_callbacks(test_listener)
+
+ result = RUNNER.run()
+
+ RUNNER.remove_callbacks(test_listener)
+
+ calls = [call('pre'),
+ call(os.path.realpath(
+ os.path.join(RUNNER.DIR,
+ '../query_compiler_filter.py')) +
+ ' --package music'),
+ call('adb shell "date -u +\'%Y-%m-%d %H:%M:%S.%N\'"'),
+ call(
+ 'timeout {timeout} "{DIR}/launch_application" "{package}" "{activity}"'
+ .format(timeout=30,
+ DIR=os.path.realpath(os.path.dirname(RUNNER.DIR)),
+ package='music',
+ activity='MainActivity',
+ timestamp='2019-07-02 23:20:06.972674825')),
+ call('post')
+ ]
+ mock_run_shell_command.assert_has_calls(calls)
+ assert result == [('TotalTime', '123')]
+ assert len(RUNNER.listeners) == 0
\ No newline at end of file
diff --git a/startop/scripts/app_startup/lib/args_utils.py b/startop/scripts/app_startup/lib/args_utils.py
new file mode 100644
index 0000000..080f3b5
--- /dev/null
+++ b/startop/scripts/app_startup/lib/args_utils.py
@@ -0,0 +1,77 @@
+import itertools
+import os
+import sys
+from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, \
+ TypeVar, Optional
+
+# local import
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__)))))
+import lib.print_utils as print_utils
+
+T = TypeVar('T')
+NamedTupleMeta = Callable[
+ ..., T] # approximation of a (S : NamedTuple<T> where S() == T) metatype.
+FilterFuncType = Callable[[NamedTuple], bool]
+
+def dict_lookup_any_key(dictionary: dict, *keys: List[Any]):
+ for k in keys:
+ if k in dictionary:
+ return dictionary[k]
+
+
+ print_utils.debug_print("None of the keys {} were in the dictionary".format(
+ keys))
+ return [None]
+
+def generate_run_combinations(named_tuple: NamedTupleMeta[T],
+ opts_dict: Dict[str, List[Optional[object]]],
+ loop_count: int = 1) -> Iterable[T]:
+ """
+ Create all possible combinations given the values in opts_dict[named_tuple._fields].
+
+ :type T: type annotation for the named_tuple type.
+ :param named_tuple: named tuple type, whose fields are used to make combinations for
+ :param opts_dict: dictionary of keys to value list. keys correspond to the named_tuple fields.
+ :param loop_count: number of repetitions.
+ :return: an iterable over named_tuple instances.
+ """
+ combinations_list = []
+ for k in named_tuple._fields:
+ # the key can be either singular or plural , e.g. 'package' or 'packages'
+ val = dict_lookup_any_key(opts_dict, k, k + "s")
+
+ # treat {'x': None} key value pairs as if it was [None]
+ # otherwise itertools.product throws an exception about not being able to iterate None.
+ combinations_list.append(val or [None])
+
+ print_utils.debug_print("opts_dict: ", opts_dict)
+ print_utils.debug_print_nd("named_tuple: ", named_tuple)
+ print_utils.debug_print("combinations_list: ", combinations_list)
+
+ for i in range(loop_count):
+ for combo in itertools.product(*combinations_list):
+ yield named_tuple(*combo)
+
+def filter_run_combinations(named_tuple: NamedTuple,
+ filters: List[FilterFuncType]) -> bool:
+ for filter in filters:
+ if filter(named_tuple):
+ return False
+ return True
+
+def generate_group_run_combinations(run_combinations: Iterable[NamedTuple],
+ dst_nt: NamedTupleMeta[T]) \
+ -> Iterable[Tuple[T, Iterable[NamedTuple]]]:
+ def group_by_keys(src_nt):
+ src_d = src_nt._asdict()
+ # now remove the keys that aren't legal in dst.
+ for illegal_key in set(src_d.keys()) - set(dst_nt._fields):
+ if illegal_key in src_d:
+ del src_d[illegal_key]
+
+ return dst_nt(**src_d)
+
+ for args_list_it in itertools.groupby(run_combinations, group_by_keys):
+ (group_key_value, args_it) = args_list_it
+ yield (group_key_value, args_it)
diff --git a/startop/scripts/app_startup/lib/args_utils_test.py b/startop/scripts/app_startup/lib/args_utils_test.py
new file mode 100644
index 0000000..4b7e0fa
--- /dev/null
+++ b/startop/scripts/app_startup/lib/args_utils_test.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+#
+
+"""Unit tests for the args_utils.py script."""
+
+import typing
+
+import args_utils
+
+def generate_run_combinations(*args):
+ # expand out the generator values so that assert x == y works properly.
+ return [i for i in args_utils.generate_run_combinations(*args)]
+
+def test_generate_run_combinations():
+ blank_nd = typing.NamedTuple('Blank')
+ assert generate_run_combinations(blank_nd, {}, 1) == [()], "empty"
+ assert generate_run_combinations(blank_nd, {'a': ['a1', 'a2']}) == [
+ ()], "empty filter"
+ a_nd = typing.NamedTuple('A', [('a', str)])
+ assert generate_run_combinations(a_nd, {'a': None}) == [(None,)], "None"
+ assert generate_run_combinations(a_nd, {'a': ['a1', 'a2']}) == [('a1',), (
+ 'a2',)], "one item"
+ assert generate_run_combinations(a_nd,
+ {'a': ['a1', 'a2'], 'b': ['b1', 'b2']}) == [
+ ('a1',), ('a2',)], \
+ "one item filter"
+ assert generate_run_combinations(a_nd, {'a': ['a1', 'a2']}, 2) == [('a1',), (
+ 'a2',), ('a1',), ('a2',)], "one item"
+ ab_nd = typing.NamedTuple('AB', [('a', str), ('b', str)])
+ assert generate_run_combinations(ab_nd,
+ {'a': ['a1', 'a2'],
+ 'b': ['b1', 'b2']}) == [ab_nd('a1', 'b1'),
+ ab_nd('a1', 'b2'),
+ ab_nd('a2', 'b1'),
+ ab_nd('a2', 'b2')], \
+ "two items"
+
+ assert generate_run_combinations(ab_nd,
+ {'as': ['a1', 'a2'],
+ 'bs': ['b1', 'b2']}) == [ab_nd('a1', 'b1'),
+ ab_nd('a1', 'b2'),
+ ab_nd('a2', 'b1'),
+ ab_nd('a2', 'b2')], \
+ "two items plural"
diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common
new file mode 100755
index 0000000..bedaa1e
--- /dev/null
+++ b/startop/scripts/app_startup/lib/common
@@ -0,0 +1,198 @@
+#!/bin/bash
+# Copyright 2018, 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.
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+ echo "Please run source build/envsetup.sh first" >&2
+ exit 1
+fi
+
+source $ANDROID_BUILD_TOP/build/envsetup.sh
+
+verbose_print() {
+ if [[ "$verbose" == "y" ]]; then
+ echo "$@" >&2
+ fi
+}
+
+remote_pidof() {
+ local procname="$1"
+ adb shell ps | grep "$procname" | awk '{print $2;}'
+}
+
+remote_pkill() {
+ local procname="$1"
+ shift
+
+ local the_pids=$(remote_pidof "$procname")
+ local pid
+
+ for pid in $the_pids; do
+ verbose_print adb shell kill "$@" "$pid"
+ adb shell kill "$@" "$pid"
+ done
+}
+
+get_activity_name() {
+ local package="$1"
+ local action_key="android.intent.action.MAIN:"
+
+ # Example query-activities output being parsed:
+ #
+ # Activity #14:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.videos/com.google.android.youtube.videos.EntryPoint
+ # Activity #15:
+ # priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+ # com.google.android.youtube/.app.honeycomb.Shell$HomeActivity
+
+ # Given package 'com.google.android.youtube' return '.app.honeycomb.Shell$HomeActivity'
+
+ local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package/")"
+ IFS="/" read -a array <<< "$activity_line"
+ local activity_name="${array[1]}"
+
+ # Activities starting with '.' are shorthand for having their package name prefixed.
+ if [[ $activity_name == .* ]]; then
+ activity_name="${package}${activity_name}"
+ fi
+ echo "$activity_name"
+}
+
+# Use with logcat_from_timestamp to skip all past log-lines.
+logcat_save_timestamp() {
+ adb shell 'date -u +"%Y-%m-%d %H:%M:%S.%N"'
+}
+
+# Roll forward logcat to only show events
+# since the specified timestamp.
+#
+# i.e. don't look at historical logcat,
+# only look at FUTURE logcat.
+#
+# First use 'logcat_save_timestamp'
+# Then do whatever action you want.
+# Then use 'logcat_from_timestamp_bg $timestamp'
+logcat_from_timestamp_bg() {
+ local timestamp="$1"
+ shift # drop timestamp from args.
+ verbose_print adb logcat -T \"$timestamp\" \"$@\"
+ adb logcat -v UTC -T "$timestamp" "$@" &
+ logcat_from_timestamp_pid=$!
+}
+
+# Starting at timestamp $2, wait until we seen pattern $3
+# or until a timeout happens in $1 seconds.
+# If successful, also echo the line that matched the pattern.
+#
+# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
+logcat_select_pattern() {
+ local timeout="$1"
+ local timestamp="$2"
+ local pattern="$3"
+
+ local logcat_fd
+
+ coproc logcat_fd {
+ kill_children_quietly() {
+ kill "$logcat_pidd"
+ wait "$logcat_pidd" 2>/dev/null
+ }
+
+ trap 'kill_children_quietly' EXIT # kill logcat when this coproc is killed.
+
+ # run logcat in the background so it can be killed.
+ logcat_from_timestamp_bg "$timestamp"
+ logcat_pidd=$logcat_from_timestamp_pid
+ wait "$logcat_pidd"
+ }
+ local logcat_pid="$!"
+ verbose_print "[LOGCAT] Spawn pid $logcat_pid"
+
+ local timeout_ts="$(date -d "now + ${timeout} seconds" '+%s')"
+ local now_ts="0"
+
+ local return_code=1
+
+ verbose_print "logcat_wait_for_pattern begin"
+
+ while read -t "$timeout" -r -u "${logcat_fd[0]}" logcat_output; do
+ if (( $VERBOSE_LOGCAT )); then
+ verbose_print "LOGCAT: $logcat_output"
+ fi
+ if [[ "$logcat_output:" == *"$pattern"* ]]; then
+ verbose_print "LOGCAT: " "$logcat_output"
+ verbose_print "WE DID SEE PATTERN" '<<' "$pattern" '>>.'
+ echo "$logcat_output"
+ return_code=0
+ break
+ fi
+ now_ts="$(date -d "now" '+%s')"
+ if (( now_ts >= timeout_ts )); then
+ verbose_print "DID TIMEOUT BEFORE SEEING ANYTHING (timeout=$timeout seconds) " '<<' "$pattern" '>>.'
+ break
+ fi
+ done
+
+ # Don't leave logcat lying around since it will keep going.
+ kill "$logcat_pid"
+ # Suppress annoying 'Terminated...' message.
+ wait "$logcat_pid" 2>/dev/null
+
+ verbose_print "[LOGCAT] $logcat_pid should be killed"
+
+ return $return_code
+}
+
+# Starting at timestamp $2, wait until we seen pattern $3
+# or until a timeout happens in $1 seconds.
+#
+# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
+logcat_wait_for_pattern() {
+ logcat_select_pattern "$@" > /dev/null
+}
+
+# Starting at timestamp $2, wait until we seen pattern $3
+# or until a timeout happens in $1 seconds.
+# If successful, extract with the regular expression pattern in #4
+# and return the first capture group.
+#
+# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
+logcat_extract_pattern() {
+ local timeout="$1"
+ local timestamp="$2"
+ local pattern="$3"
+ local re_pattern="$4"
+
+ local result
+ local exit_code
+
+ result="$(logcat_select_pattern "$@")"
+ exit_code=$?
+
+ if [[ $exit_code -ne 0 ]]; then
+ return $exit_code
+ fi
+
+ echo "$result" | sed 's/'"$re_pattern"'/\1/g'
+}
+
+# Join array
+# FOO=(a b c)
+# join_by , "${FOO[@]}" #a,b,c
+join_by() {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
diff --git a/startop/scripts/app_startup/lib/data_frame.py b/startop/scripts/app_startup/lib/data_frame.py
new file mode 100644
index 0000000..20a2308
--- /dev/null
+++ b/startop/scripts/app_startup/lib/data_frame.py
@@ -0,0 +1,201 @@
+import itertools
+from typing import Dict, List
+
+class DataFrame:
+ """Table-like class for storing a 2D cells table with named columns."""
+ def __init__(self, data: Dict[str, List[object]] = {}):
+ """
+ Create a new DataFrame from a dictionary (keys = headers,
+ values = columns).
+ """
+ self._headers = [i for i in data.keys()]
+ self._rows = []
+
+ row_num = 0
+
+ def get_data_row(idx):
+ r = {}
+ for header, header_data in data.items():
+
+ if not len(header_data) > idx:
+ continue
+
+ r[header] = header_data[idx]
+
+ return r
+
+ while True:
+ row_dict = get_data_row(row_num)
+ if len(row_dict) == 0:
+ break
+
+ self._append_row(row_dict.keys(), row_dict.values())
+ row_num = row_num + 1
+
+ def concat_rows(self, other: 'DataFrame') -> None:
+ """
+ In-place concatenate rows of other into the rows of the
+ current DataFrame.
+
+ None is added in pre-existing cells if new headers
+ are introduced.
+ """
+ other_datas = other._data_only()
+
+ other_headers = other.headers
+
+ for d in other_datas:
+ self._append_row(other_headers, d)
+
+ def _append_row(self, headers: List[str], data: List[object]):
+ new_row = {k:v for k,v in zip(headers, data)}
+ self._rows.append(new_row)
+
+ for header in headers:
+ if not header in self._headers:
+ self._headers.append(header)
+
+ def __repr__(self):
+# return repr(self._rows)
+ repr = ""
+
+ header_list = self._headers_only()
+
+ row_format = u""
+ for header in header_list:
+ row_format = row_format + u"{:>%d}" %(len(header) + 1)
+
+ repr = row_format.format(*header_list) + "\n"
+
+ for v in self._data_only():
+ repr = repr + row_format.format(*v) + "\n"
+
+ return repr
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return self.headers == other.headers and self.data_table == other.data_table
+ else:
+ print("wrong instance", other.__class__)
+ return False
+
+ @property
+ def headers(self) -> List[str]:
+ return [i for i in self._headers_only()]
+
+ @property
+ def data_table(self) -> List[List[object]]:
+ return list(self._data_only())
+
+ @property
+ def data_table_transposed(self) -> List[List[object]]:
+ return list(self._transposed_data())
+
+ @property
+ def data_row_len(self) -> int:
+ return len(self._rows)
+
+ def data_row_at(self, idx) -> List[object]:
+ """
+ Return a single data row at the specified index (0th based).
+
+ Accepts negative indices, e.g. -1 is last row.
+ """
+ row_dict = self._rows[idx]
+ l = []
+
+ for h in self._headers_only():
+ l.append(row_dict.get(h)) # Adds None in blank spots.
+
+ return l
+
+ def copy(self) -> 'DataFrame':
+ """
+ Shallow copy of this DataFrame.
+ """
+ return self.repeat(count=0)
+
+ def repeat(self, count: int) -> 'DataFrame':
+ """
+ Returns a new DataFrame where each row of this dataframe is repeated count times.
+ A repeat of a row is adjacent to other repeats of that same row.
+ """
+ df = DataFrame()
+ df._headers = self._headers.copy()
+
+ rows = []
+ for row in self._rows:
+ for i in range(count):
+ rows.append(row.copy())
+
+ df._rows = rows
+
+ return df
+
+ def merge_data_columns(self, other: 'DataFrame'):
+ """
+ Merge self and another DataFrame by adding the data from other column-wise.
+ For any headers that are the same, data from 'other' is preferred.
+ """
+ for h in other._headers:
+ if not h in self._headers:
+ self._headers.append(h)
+
+ append_rows = []
+
+ for self_dict, other_dict in itertools.zip_longest(self._rows, other._rows):
+ if not self_dict:
+ d = {}
+ append_rows.append(d)
+ else:
+ d = self_dict
+
+ d_other = other_dict
+ if d_other:
+ for k,v in d_other.items():
+ d[k] = v
+
+ for r in append_rows:
+ self._rows.append(r)
+
+ def data_row_reduce(self, fnc) -> 'DataFrame':
+ """
+ Reduces the data row-wise by applying the fnc to each row (column-wise).
+ Empty cells are skipped.
+
+ fnc(Iterable[object]) -> object
+ fnc is applied over every non-empty cell in that column (descending row-wise).
+
+ Example:
+ DataFrame({'a':[1,2,3]}).data_row_reduce(sum) == DataFrame({'a':[6]})
+
+ Returns a new single-row DataFrame.
+ """
+ df = DataFrame()
+ df._headers = self._headers.copy()
+
+ def yield_by_column(header_key):
+ for row_dict in self._rows:
+ val = row_dict.get(header_key)
+ if val:
+ yield val
+
+ new_row_dict = {}
+ for h in df._headers:
+ cell_value = fnc(yield_by_column(h))
+ new_row_dict[h] = cell_value
+
+ df._rows = [new_row_dict]
+ return df
+
+ def _headers_only(self):
+ return self._headers
+
+ def _data_only(self):
+ row_len = len(self._rows)
+
+ for i in range(row_len):
+ yield self.data_row_at(i)
+
+ def _transposed_data(self):
+ return zip(*self._data_only())
\ No newline at end of file
diff --git a/startop/scripts/app_startup/lib/data_frame_test.py b/startop/scripts/app_startup/lib/data_frame_test.py
new file mode 100644
index 0000000..1cbc1cb
--- /dev/null
+++ b/startop/scripts/app_startup/lib/data_frame_test.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+#
+
+"""Unit tests for the data_frame.py script."""
+
+from data_frame import DataFrame
+
+def test_data_frame():
+ # trivial empty data frame
+ df = DataFrame()
+ assert df.headers == []
+ assert df.data_table == []
+ assert df.data_table_transposed == []
+
+ # common case, same number of values in each place.
+ df = DataFrame({'TotalTime_ms': [1, 2, 3], 'Displayed_ms': [4, 5, 6]})
+ assert df.headers == ['TotalTime_ms', 'Displayed_ms']
+ assert df.data_table == [[1, 4], [2, 5], [3, 6]]
+ assert df.data_table_transposed == [(1, 2, 3), (4, 5, 6)]
+
+ # varying num values.
+ df = DataFrame({'many': [1, 2], 'none': []})
+ assert df.headers == ['many', 'none']
+ assert df.data_table == [[1, None], [2, None]]
+ assert df.data_table_transposed == [(1, 2), (None, None)]
+
+ df = DataFrame({'many': [], 'none': [1, 2]})
+ assert df.headers == ['many', 'none']
+ assert df.data_table == [[None, 1], [None, 2]]
+ assert df.data_table_transposed == [(None, None), (1, 2)]
+
+ # merge multiple data frames
+ df = DataFrame()
+ df.concat_rows(DataFrame())
+ assert df.headers == []
+ assert df.data_table == []
+ assert df.data_table_transposed == []
+
+ df = DataFrame()
+ df2 = DataFrame({'TotalTime_ms': [1, 2, 3], 'Displayed_ms': [4, 5, 6]})
+
+ df.concat_rows(df2)
+ assert df.headers == ['TotalTime_ms', 'Displayed_ms']
+ assert df.data_table == [[1, 4], [2, 5], [3, 6]]
+ assert df.data_table_transposed == [(1, 2, 3), (4, 5, 6)]
+
+ df = DataFrame({'TotalTime_ms': [1, 2]})
+ df2 = DataFrame({'Displayed_ms': [4, 5]})
+
+ df.concat_rows(df2)
+ assert df.headers == ['TotalTime_ms', 'Displayed_ms']
+ assert df.data_table == [[1, None], [2, None], [None, 4], [None, 5]]
+
+ df = DataFrame({'TotalTime_ms': [1, 2]})
+ df2 = DataFrame({'TotalTime_ms': [3, 4], 'Displayed_ms': [5, 6]})
+
+ df.concat_rows(df2)
+ assert df.headers == ['TotalTime_ms', 'Displayed_ms']
+ assert df.data_table == [[1, None], [2, None], [3, 5], [4, 6]]
+
+ # data_row_at
+ df = DataFrame({'TotalTime_ms': [1, 2, 3], 'Displayed_ms': [4, 5, 6]})
+ assert df.data_row_at(-1) == [3, 6]
+ assert df.data_row_at(2) == [3, 6]
+ assert df.data_row_at(1) == [2, 5]
+
+ # repeat
+ df = DataFrame({'TotalTime_ms': [1], 'Displayed_ms': [4]})
+ df2 = DataFrame({'TotalTime_ms': [1, 1, 1], 'Displayed_ms': [4, 4, 4]})
+ assert df.repeat(3) == df2
+
+ # repeat
+ df = DataFrame({'TotalTime_ms': [1, 1, 1], 'Displayed_ms': [4, 4, 4]})
+ assert df.data_row_len == 3
+ df = DataFrame({'TotalTime_ms': [1, 1]})
+ assert df.data_row_len == 2
+
+ # repeat
+ df = DataFrame({'TotalTime_ms': [1, 1, 1], 'Displayed_ms': [4, 4, 4]})
+ assert df.data_row_len == 3
+ df = DataFrame({'TotalTime_ms': [1, 1]})
+ assert df.data_row_len == 2
+
+ # data_row_reduce
+ df = DataFrame({'TotalTime_ms': [1, 1, 1], 'Displayed_ms': [4, 4, 4]})
+ df_sum = DataFrame({'TotalTime_ms': [3], 'Displayed_ms': [12]})
+ assert df.data_row_reduce(sum) == df_sum
+
+ # merge_data_columns
+ df = DataFrame({'TotalTime_ms': [1, 2, 3]})
+ df2 = DataFrame({'Displayed_ms': [3, 4, 5, 6]})
+
+ df.merge_data_columns(df2)
+ assert df == DataFrame(
+ {'TotalTime_ms': [1, 2, 3], 'Displayed_ms': [3, 4, 5, 6]})
+
+ df = DataFrame({'TotalTime_ms': [1, 2, 3]})
+ df2 = DataFrame({'Displayed_ms': [3, 4]})
+
+ df.merge_data_columns(df2)
+ assert df == DataFrame(
+ {'TotalTime_ms': [1, 2, 3], 'Displayed_ms': [3, 4]})
+
+ df = DataFrame({'TotalTime_ms': [1, 2, 3]})
+ df2 = DataFrame({'TotalTime_ms': [10, 11]})
+
+ df.merge_data_columns(df2)
+ assert df == DataFrame({'TotalTime_ms': [10, 11, 3]})
+
+ df = DataFrame({'TotalTime_ms': []})
+ df2 = DataFrame({'TotalTime_ms': [10, 11]})
+
+ df.merge_data_columns(df2)
+ assert df == DataFrame({'TotalTime_ms': [10, 11]})
diff --git a/startop/scripts/app_startup/lib/perfetto_trace_collector.py b/startop/scripts/app_startup/lib/perfetto_trace_collector.py
new file mode 100644
index 0000000..9ffb349
--- /dev/null
+++ b/startop/scripts/app_startup/lib/perfetto_trace_collector.py
@@ -0,0 +1,166 @@
+# Copyright 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.
+
+"""Class to collector perfetto trace."""
+import datetime
+import os
+import re
+import sys
+import time
+from datetime import timedelta
+from typing import Optional, List, Tuple
+
+# global variables
+DIR = os.path.abspath(os.path.dirname(__file__))
+
+sys.path.append(os.path.dirname(os.path.dirname(DIR)))
+
+import app_startup.lib.adb_utils as adb_utils
+from app_startup.lib.app_runner import AppRunner, AppRunnerListener
+import lib.print_utils as print_utils
+import lib.logcat_utils as logcat_utils
+import iorap.lib.iorapd_utils as iorapd_utils
+
+class PerfettoTraceCollector(AppRunnerListener):
+ """ Class to collect perfetto trace.
+
+ To set trace duration of perfetto, change the 'trace_duration_ms'.
+ To pull the generated perfetto trace on device, set the 'output'.
+ """
+ TRACE_FILE_SUFFIX = 'perfetto_trace.pb'
+ TRACE_DURATION_PROP = 'iorapd.perfetto.trace_duration_ms'
+ MS_PER_SEC = 1000
+ DEFAULT_TRACE_DURATION = timedelta(milliseconds=5000) # 5 seconds
+ _COLLECTOR_TIMEOUT_MULTIPLIER = 10 # take the regular timeout and multiply
+
+ def __init__(self,
+ package: str,
+ activity: Optional[str],
+ compiler_filter: Optional[str],
+ timeout: Optional[int],
+ simulate: bool,
+ trace_duration: timedelta = DEFAULT_TRACE_DURATION,
+ save_destination_file_path: Optional[str] = None):
+ """ Initialize the perfetto trace collector. """
+ self.app_runner = AppRunner(package,
+ activity,
+ compiler_filter,
+ timeout,
+ simulate)
+ self.app_runner.add_callbacks(self)
+
+ self.trace_duration = trace_duration
+ self.save_destination_file_path = save_destination_file_path
+
+ def purge_file(self, suffix: str) -> None:
+ print_utils.debug_print('iorapd-perfetto: purge file in ' +
+ self._get_remote_path())
+ adb_utils.delete_file_on_device(self._get_remote_path())
+
+ def run(self) -> Optional[List[Tuple[str]]]:
+ """Runs an app.
+
+ Returns:
+ A list of (metric, value) tuples.
+ """
+ return self.app_runner.run()
+
+ def preprocess(self):
+ # Sets up adb environment.
+ adb_utils.root()
+ adb_utils.disable_selinux()
+ time.sleep(1)
+
+ # Kill any existing process of this app
+ adb_utils.pkill(self.app_runner.package)
+
+ # Remove existing trace and compiler files
+ self.purge_file(PerfettoTraceCollector.TRACE_FILE_SUFFIX)
+
+ # Set perfetto trace duration prop to milliseconds.
+ adb_utils.set_prop(PerfettoTraceCollector.TRACE_DURATION_PROP,
+ int(self.trace_duration.total_seconds()*
+ PerfettoTraceCollector.MS_PER_SEC))
+
+ if not iorapd_utils.stop_iorapd():
+ raise RuntimeError('Cannot stop iorapd!')
+
+ if not iorapd_utils.enable_iorapd_perfetto():
+ raise RuntimeError('Cannot enable perfetto!')
+
+ if not iorapd_utils.disable_iorapd_readahead():
+ raise RuntimeError('Cannot disable readahead!')
+
+ if not iorapd_utils.start_iorapd():
+ raise RuntimeError('Cannot start iorapd!')
+
+ # Drop all caches to get cold starts.
+ adb_utils.vm_drop_cache()
+
+ def postprocess(self, pre_launch_timestamp: str):
+ # Kill any existing process of this app
+ adb_utils.pkill(self.app_runner.package)
+
+ iorapd_utils.disable_iorapd_perfetto()
+
+ if self.save_destination_file_path is not None:
+ adb_utils.pull_file(self._get_remote_path(),
+ self.save_destination_file_path)
+
+ def metrics_selector(self, am_start_output: str,
+ pre_launch_timestamp: str) -> str:
+ """Parses the metric after app startup by reading from logcat in a blocking
+ manner until all metrics have been found".
+
+ Returns:
+ An empty string because the metric needs no further parsing.
+ """
+ if not self._wait_for_perfetto_trace(pre_launch_timestamp):
+ raise RuntimeError('Could not save perfetto app trace file!')
+
+ return ''
+
+ def _wait_for_perfetto_trace(self, pre_launch_timestamp) -> Optional[str]:
+ """ Waits for the perfetto trace being saved to file.
+
+ The string is in the format of r".*Perfetto TraceBuffer saved to file:
+ <file path>.*"
+
+ Returns:
+ the string what the program waits for. If the string doesn't show up,
+ return None.
+ """
+ pattern = re.compile(r'.*Perfetto TraceBuffer saved to file: {}.*'.
+ format(self._get_remote_path()))
+
+ # The pre_launch_timestamp is longer than what the datetime can parse. Trim
+ # last three digits to make them align. For example:
+ # 2019-07-02 23:20:06.972674825999 -> 2019-07-02 23:20:06.972674825
+ assert len(pre_launch_timestamp) == len('2019-07-02 23:20:06.972674825')
+ timestamp = datetime.datetime.strptime(pre_launch_timestamp[:-3],
+ '%Y-%m-%d %H:%M:%S.%f')
+
+ # The timeout of perfetto trace is longer than the normal app run timeout.
+ timeout_dt = self.app_runner.timeout * PerfettoTraceCollector._COLLECTOR_TIMEOUT_MULTIPLIER
+ timeout_end = timestamp + datetime.timedelta(seconds=timeout_dt)
+
+ return logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
+ pattern,
+ timeout_end)
+
+ def _get_remote_path(self):
+ # For example: android.music%2Fmusic.TopLevelActivity.perfetto_trace.pb
+ return iorapd_utils._iorapd_path_to_data_file(self.app_runner.package,
+ self.app_runner.activity,
+ PerfettoTraceCollector.TRACE_FILE_SUFFIX)
diff --git a/startop/scripts/app_startup/lib/perfetto_trace_collector_test.py b/startop/scripts/app_startup/lib/perfetto_trace_collector_test.py
new file mode 100644
index 0000000..8d94fc5
--- /dev/null
+++ b/startop/scripts/app_startup/lib/perfetto_trace_collector_test.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+
+"""Unit tests for the data_frame.py script."""
+import os
+import sys
+from pathlib import Path
+from datetime import timedelta
+
+from mock import call, patch
+from perfetto_trace_collector import PerfettoTraceCollector
+
+sys.path.append(Path(os.path.realpath(__file__)).parents[2])
+from app_startup.lib.app_runner import AppRunner
+
+RUNNER = PerfettoTraceCollector(package='music',
+ activity='MainActivity',
+ compiler_filter=None,
+ timeout=10,
+ simulate=False,
+ trace_duration = timedelta(milliseconds=1000),
+ # No actual file will be created. Just to
+ # check the command.
+ save_destination_file_path='/tmp/trace.pb')
+
+def _mocked_run_shell_command(*args, **kwargs):
+ if args[0] == 'adb shell ps | grep "music" | awk \'{print $2;}\'':
+ return (True, '9999')
+ else:
+ return (True, '')
+
+@patch('lib.logcat_utils.blocking_wait_for_logcat_pattern')
+@patch('lib.cmd_utils.run_shell_command')
+def test_perfetto_trace_collector_preprocess(mock_run_shell_command,
+ mock_blocking_wait_for_logcat_pattern):
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ mock_blocking_wait_for_logcat_pattern.return_value = "Succeed!"
+
+ RUNNER.preprocess()
+
+ calls = [call('adb root'),
+ call('adb shell "getenforce"'),
+ call('adb shell "setenforce 0"'),
+ call('adb shell "stop"'),
+ call('adb shell "start"'),
+ call('adb wait-for-device'),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"'),
+ call(
+ 'adb shell "[[ -f \'/data/misc/iorapd/music%2FMainActivity.perfetto_trace.pb\' ]] '
+ '&& rm -f \'/data/misc/iorapd/music%2FMainActivity.perfetto_trace.pb\' || exit 0"'),
+ call('adb shell "setprop "iorapd.perfetto.trace_duration_ms" "1000""'),
+ call(
+ 'bash -c "source {}; iorapd_stop"'.format(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call(
+ 'bash -c "source {}; iorapd_perfetto_enable"'.format(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call(
+ 'bash -c "source {}; iorapd_readahead_disable"'.format(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call(
+ 'bash -c "source {}; iorapd_start"'.format(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call('adb shell "echo 3 > /proc/sys/vm/drop_caches"')]
+
+ mock_run_shell_command.assert_has_calls(calls)
+
+@patch('lib.logcat_utils.blocking_wait_for_logcat_pattern')
+@patch('lib.cmd_utils.run_shell_command')
+def test_perfetto_trace_collector_postprocess(mock_run_shell_command,
+ mock_blocking_wait_for_logcat_pattern):
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ mock_blocking_wait_for_logcat_pattern.return_value = "Succeed!"
+
+ RUNNER.postprocess('2019-07-02 23:20:06.972674825')
+
+ calls = [call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"'),
+ call(
+ 'bash -c "source {}; iorapd_perfetto_disable"'.format(
+ AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call('adb pull '
+ '"/data/misc/iorapd/music%2FMainActivity.perfetto_trace.pb" '
+ '"/tmp/trace.pb"')]
+
+ mock_run_shell_command.assert_has_calls(calls)
diff --git a/startop/scripts/app_startup/parse_metrics b/startop/scripts/app_startup/parse_metrics
new file mode 100755
index 0000000..3fa1462
--- /dev/null
+++ b/startop/scripts/app_startup/parse_metrics
@@ -0,0 +1,215 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+usage() {
+ cat <<EOF
+Usage: launch_application package activity | parse_metrics --package <name> --timestamp <timestamp> [OPTIONS]...
+
+ Reads from stdin the result of 'am start' metrics. May also parse logcat
+ for additional metrics.
+
+ Output form:
+
+ MetricName_unit=numeric_value
+ MetricName2_unit=numeric_value2
+
+ This may block until all desired metrics are parsed from logcat.
+ To get a list of metrics without doing real parsing, use --simulate.
+
+ To add package-specific metrics, add a script called 'metrics/\$full_package_name'
+ that exposes additional metrics in same way as above.
+
+ (required)
+ -p, --package <name> package of the app that is being used
+ -ts, --timestamp <name> logcat timestamp [only looks at logcat entries after this timestamp].
+
+ (optional)
+ -s, --simulate prints dummy values instead of real metrics
+ -a, --activity <name> activity to use (default: inferred)
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -t, --timeout <sec> how many seconds to timeout when trying to wait for logcat to change
+ -rfd, --reportfullydrawn wait for report fully drawn (default: off)
+EOF
+}
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/lib/common"
+
+report_fully_drawn="n"
+package=""
+activity=""
+timeout=5
+simulate="n"
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -v|--verbose)
+ export verbose="y"
+ ;;
+ -t|--timeout)
+ timeout="$2"
+ shift
+ ;;
+ -ts|--timestamp)
+ timestamp="$2"
+ shift
+ ;;
+ -s|--simulate)
+ simulate="y"
+ ;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ ;;
+
+
+ *)
+ echo "Invalid argument: $1" >&2
+ exit 1
+ esac
+ shift
+ done
+}
+
+# Main entry point
+if [[ $# -eq 0 ]]; then
+ usage
+ exit 1
+else
+ parse_arguments "$@"
+
+ # if we do not have have package exit early with an error
+ [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 64
+
+ # ignore timestamp for --simulate. it's optional.
+ if [[ $simulate == y ]]; then
+ timestamp=0
+ fi
+
+ # if we do not have timestamp, exit early with an error
+ [[ "$timestamp" == "" ]] && echo "--timestamp not specified" 1>&2 && exit 64
+
+ if [[ "$activity" == "" ]] && [[ "$simulate" != "y" ]]; then
+ activity="$(get_activity_name "$package")"
+ if [[ "$activity" == "" ]]; then
+ echo "Activity name could not be found, invalid package name?" 1>&2
+ exit 64
+ else
+ verbose_print "Activity name inferred: " "$activity"
+ fi
+ fi
+fi
+
+parse_metric_from_logcat() {
+ local metric_name="$1"
+ local pattern="$2"
+ local re_pattern="$3"
+ local retcode
+ local result
+ local sec
+ local ms
+
+ # parse logcat for 'Displayed...' and that other one...
+
+ # 05-06 14:34:08.854 29460 29481 I ActivityTaskManager: Displayed com.google.android.dialer/.extensions.GoogleDialtactsActivity: +361ms
+ verbose_print "parse_metric_from_logcat: $re_pattern"
+
+
+ echo -ne "$metric_name="
+
+ if [[ $simulate == y ]]; then
+ echo "-1"
+ return 0
+ fi
+
+ result="$(logcat_extract_pattern "$timeout" "$timestamp" "$pattern" "$re_pattern")"
+ retcode=$?
+
+ if [[ $retcode -ne 0 ]]; then
+ # Timed out before finding the pattern. Could also mean the pattern is wrong.
+ echo "Parse $re_pattern from logcat TIMED OUT after $timeout seconds." >&2
+ echo "-$?"
+ return $retcode
+ fi
+
+ # "10s123ms" -> "10s123"
+ result=${result/ms/}
+ if [[ $result =~ s ]]; then
+ ms=${result/*s/}
+ sec=${result/s*/}
+ else
+ sec=0
+ ms=$result
+ fi
+ ((result=sec*1000+ms))
+
+ echo "$result"
+ return $retcode
+}
+
+
+total_time="-1"
+if [[ $simulate != y ]]; then
+ verbose_print 'logcat timestamp NOW: ' $(logcat_save_timestamp)
+
+ # parse stdin for 'am start' result
+ while read -t "$timeout" -r input_line; do
+ verbose_print 'stdin:' "$input_line"
+ if [[ $input_line == *TotalTime:* ]]; then
+ total_time="$(echo "$input_line" | sed 's/TotalTime: \([[:digit:]]\+\)/\1/g')"
+ # but keep reading the rest from stdin until <EOF>
+ fi
+ done
+fi
+
+echo "TotalTime_ms=$total_time"
+
+# parse logcat for 'Displayed...' and that other one...
+
+# 05-06 14:34:08.854 29460 29481 I ActivityTaskManager: Displayed com.google.android.dialer/.extensions.GoogleDialtactsActivity: +361ms
+pattern="ActivityTaskManager: Displayed ${package}"
+re_pattern='.*Displayed[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
+
+parse_metric_from_logcat "Displayed_ms" "$pattern" "$re_pattern"
+
+# Only track ReportFullyDrawn with --reportfullydrawn/-rfd flags
+if [[ $report_fully_drawn == y ]]; then
+ # 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
+ pattern="ActivityTaskManager: Fully drawn ${package}"
+ #re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
+ re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
+
+ parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+fi
+
+# also call into package-specific scripts if there are additional metrics
+if [[ -x "$DIR/metrics/$package" ]]; then
+ source "$DIR/metrics/$package" "$timestamp"
+else
+ verbose_print parse_metrics: no per-package metrics script found at "$DIR/metrics/$package"
+fi
diff --git a/startop/scripts/app_startup/query_compiler_filter.py b/startop/scripts/app_startup/query_compiler_filter.py
new file mode 100755
index 0000000..ea14264
--- /dev/null
+++ b/startop/scripts/app_startup/query_compiler_filter.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+
+#
+#
+# Query the current compiler filter for an application by its package name.
+# (By parsing the results of the 'adb shell dumpsys package $package' command).
+# The output is a string "$compilation_filter $compilation_reason $isa".
+#
+# See --help for more details.
+#
+# -----------------------------------
+#
+# Sample usage:
+#
+# $> ./query_compiler_filter.py --package com.google.android.calculator
+# speed-profile unknown arm64
+#
+
+import argparse
+import os
+import re
+import sys
+
+# TODO: refactor this with a common library file with analyze_metrics.py
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+import lib.cmd_utils as cmd_utils
+import lib.print_utils as print_utils
+
+from typing import List, NamedTuple, Iterable
+
+_DEBUG_FORCE = None # Ignore -d/--debug if this is not none.
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Query the compiler filter for a package.")
+ # argparse considers args starting with - and -- optional in --help, even though required=True.
+ # by using a named argument group --help will clearly say that it's required instead of optional.
+ required_named = parser.add_argument_group('required named arguments')
+ required_named.add_argument('-p', '--package', action='store', dest='package', help='package of the application', required=True)
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-i', '--isa', '--instruction-set', action='store', dest='instruction_set', help='which instruction set to select. defaults to the first one available if not specified.', choices=('arm64', 'arm', 'x86_64', 'x86'))
+ optional_named.add_argument('-s', '--simulate', dest='simulate', action='store_true', help='Print which commands will run, but don\'t run the apps')
+ optional_named.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output')
+
+ return parser.parse_args(argv)
+
+def remote_dumpsys_package(package: str, simulate: bool) -> str:
+ # --simulate is used for interactive debugging/development, but also for the unit test.
+ if simulate:
+ return """
+Dexopt state:
+ [%s]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ arm64: [status=speed-profile] [reason=unknown]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ arm: [status=speed] [reason=first-boot]
+ path: /data/app/%s-D7s8PLidqqEq7Jc7UH_a5A==/base.apk
+ x86: [status=quicken] [reason=install]
+""" %(package, package, package, package)
+
+ code, res = cmd_utils.execute_arbitrary_command(['adb', 'shell', 'dumpsys',
+ 'package', package],
+ simulate=False,
+ timeout=5,
+ shell=False)
+ if code:
+ return res
+ else:
+ raise AssertionError("Failed to dumpsys package, errors = %s", res)
+
+ParseTree = NamedTuple('ParseTree', [('label', str), ('children', List['ParseTree'])])
+DexoptState = ParseTree # With the Dexopt state: label
+ParseResult = NamedTuple('ParseResult', [('remainder', List[str]), ('tree', ParseTree)])
+
+def find_parse_subtree(parse_tree: ParseTree, match_regex: str) -> ParseTree:
+ if re.match(match_regex, parse_tree.label):
+ return parse_tree
+
+ for node in parse_tree.children:
+ res = find_parse_subtree(node, match_regex)
+ if res:
+ return res
+
+ return None
+
+def find_parse_children(parse_tree: ParseTree, match_regex: str) -> Iterable[ParseTree]:
+ for node in parse_tree.children:
+ if re.match(match_regex, node.label):
+ yield node
+
+def parse_tab_subtree(label: str, str_lines: List[str], separator=' ', indent=-1) -> ParseResult:
+ children = []
+
+ get_indent_level = lambda line: len(line) - len(line.lstrip())
+
+ line_num = 0
+
+ keep_going = True
+ while keep_going:
+ keep_going = False
+
+ for line_num in range(len(str_lines)):
+ line = str_lines[line_num]
+ current_indent = get_indent_level(line)
+
+ print_utils.debug_print("INDENT=%d, LINE=%s" %(current_indent, line))
+
+ current_label = line.lstrip()
+
+ # skip empty lines
+ if line.lstrip() == "":
+ continue
+
+ if current_indent > indent:
+ parse_result = parse_tab_subtree(current_label, str_lines[line_num+1::], separator, current_indent)
+ str_lines = parse_result.remainder
+ children.append(parse_result.tree)
+ keep_going = True
+ else:
+ # current_indent <= indent
+ keep_going = False
+
+ break
+
+ new_remainder = str_lines[line_num::]
+ print_utils.debug_print("NEW REMAINDER: ", new_remainder)
+
+ parse_tree = ParseTree(label, children)
+ return ParseResult(new_remainder, parse_tree)
+
+def parse_tab_tree(str_tree: str, separator=' ', indentation_level=-1) -> ParseTree:
+
+ label = None
+ lst = []
+
+ line_num = 0
+ line_lst = str_tree.split("\n")
+
+ return parse_tab_subtree("", line_lst, separator, indentation_level).tree
+
+def parse_dexopt_state(dumpsys_tree: ParseTree) -> DexoptState:
+ res = find_parse_subtree(dumpsys_tree, "Dexopt(\s+)state[:]?")
+ if not res:
+ raise AssertionError("Could not find the Dexopt state")
+ return res
+
+def find_first_compiler_filter(dexopt_state: DexoptState, package: str, instruction_set: str) -> str:
+ lst = find_all_compiler_filters(dexopt_state, package)
+
+ print_utils.debug_print("all compiler filters: ", lst)
+
+ for compiler_filter_info in lst:
+ if not instruction_set:
+ return compiler_filter_info
+
+ if compiler_filter_info.isa == instruction_set:
+ return compiler_filter_info
+
+ return None
+
+CompilerFilterInfo = NamedTuple('CompilerFilterInfo', [('isa', str), ('status', str), ('reason', str)])
+
+def find_all_compiler_filters(dexopt_state: DexoptState, package: str) -> List[CompilerFilterInfo]:
+
+ lst = []
+ package_tree = find_parse_subtree(dexopt_state, re.escape("[%s]" %package))
+
+ if not package_tree:
+ raise AssertionError("Could not find any package subtree for package %s" %(package))
+
+ print_utils.debug_print("package tree: ", package_tree)
+
+ for path_tree in find_parse_children(package_tree, "path: "):
+ print_utils.debug_print("path tree: ", path_tree)
+
+ matchre = re.compile("([^:]+):\s+\[status=([^\]]+)\]\s+\[reason=([^\]]+)\]")
+
+ for isa_node in find_parse_children(path_tree, matchre):
+
+ matches = re.match(matchre, isa_node.label).groups()
+
+ info = CompilerFilterInfo(*matches)
+ lst.append(info)
+
+ return lst
+
+def main() -> int:
+ opts = parse_options()
+ cmd_utils._debug = opts.debug
+ if _DEBUG_FORCE is not None:
+ cmd_utils._debug = _DEBUG_FORCE
+ print_utils.debug_print("parsed options: ", opts)
+
+ # Note: This can often 'fail' if the package isn't actually installed.
+ package_dumpsys = remote_dumpsys_package(opts.package, opts.simulate)
+ print_utils.debug_print("package dumpsys: ", package_dumpsys)
+ dumpsys_parse_tree = parse_tab_tree(package_dumpsys, package_dumpsys)
+ print_utils.debug_print("parse tree: ", dumpsys_parse_tree)
+ dexopt_state = parse_dexopt_state(dumpsys_parse_tree)
+
+ filter = find_first_compiler_filter(dexopt_state, opts.package, opts.instruction_set)
+
+ if filter:
+ print(filter.status, end=' ')
+ print(filter.reason, end=' ')
+ print(filter.isa)
+ else:
+ print("ERROR: Could not find any compiler-filter for package %s, isa %s" %(opts.package, opts.instruction_set), file=sys.stderr)
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/query_compiler_filter_test.py b/startop/scripts/app_startup/query_compiler_filter_test.py
new file mode 100755
index 0000000..a751a43
--- /dev/null
+++ b/startop/scripts/app_startup/query_compiler_filter_test.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018, 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.
+#
+
+"""
+Unit tests for the query_compiler_filter.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./query_compiler_filter.py
+ $> pytest query_compiler_filter.py
+ $> python -m pytest query_compiler_filter.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+from contextlib import contextmanager
+import io
+import shlex
+import sys
+import typing
+
+# pip imports
+import pytest
+
+# local imports
+import query_compiler_filter as qcf
+
+@contextmanager
+def redirect_stdout_stderr():
+ """Redirect stdout/stderr to a new StringIO for duration of context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ new_stdout = io.StringIO()
+ sys.stdout = new_stdout
+ new_stderr = io.StringIO()
+ sys.stderr = new_stderr
+ try:
+ yield (new_stdout, new_stderr)
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ # Seek back to the beginning so we can read whatever was written into it.
+ new_stdout.seek(0)
+ new_stderr.seek(0)
+
+@contextmanager
+def replace_argv(argv):
+ """ Temporarily replace argv for duration of this context."""
+ old_argv = sys.argv
+ sys.argv = [sys.argv[0]] + argv
+ try:
+ yield
+ finally:
+ sys.argv = old_argv
+
+def exec_main(argv):
+ """Run the query_compiler_filter main function with the provided arguments.
+
+ Returns the stdout result when successful, assertion failure otherwise.
+ """
+ try:
+ with redirect_stdout_stderr() as (the_stdout, the_stderr):
+ with replace_argv(argv):
+ code = qcf.main()
+ assert 0 == code, the_stderr.readlines()
+
+ all_lines = the_stdout.readlines()
+ return "".join(all_lines)
+ finally:
+ the_stdout.close()
+ the_stderr.close()
+
+def test_query_compiler_filter():
+ # no --instruction-set specified: provide whatever was the 'first' filter.
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps']) == \
+ "speed-profile unknown arm64\n"
+
+ # specifying an instruction set finds the exact compiler filter match.
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'arm64']) == \
+ "speed-profile unknown arm64\n"
+
+ assert exec_main(['--simulate',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'arm']) == \
+ "speed first-boot arm\n"
+
+ assert exec_main(['--simulate',
+ '--debug',
+ '--package', 'com.google.android.apps.maps',
+ '--instruction-set', 'x86']) == \
+ "quicken install x86\n"
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
new file mode 100755
index 0000000..31f6253
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -0,0 +1,487 @@
+#!/bin/bash
+#
+# Copyright 2018, 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.
+
+usage() {
+ cat <<EOF
+Usage: run_app_with_prefetch --package <name> [OPTIONS]...
+
+ -p, --package <name> package of the app to test
+ -a, --activity <name> activity to use
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -i, --input <file> trace file protobuf (default 'TraceFile.pb')
+ -r, --readahead <mode> cold, warm, fadvise, mlock (default 'warm')
+ -w, --when <when> aot or jit (default 'jit')
+ -c, --count <count> how many times to run (default 1)
+ -s, --sleep <sec> how long to sleep after readahead
+ -t, --timeout <sec> how many seconds to timeout in between each app run (default 10)
+ -o, --output <file.csv> what file to write the performance results into as csv (default stdout)
+EOF
+}
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "$DIR/../iorap/common"
+
+report_fully_drawn="n"
+needs_trace_file="n"
+input_file=""
+package=""
+mode='warm'
+count=2
+sleep_time=2
+timeout=10
+output="" # stdout by default
+when="jit"
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -i|--input)
+ input_file="$2"
+ shift
+ ;;
+ -v|--verbose)
+ export verbose="y"
+ ;;
+ -r|--readahead)
+ mode="$2"
+ shift
+ ;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ shift
+ ;;
+ -c|--count)
+ count="$2"
+ ((count+=1))
+ shift
+ ;;
+ -s|--sleep)
+ sleep_time="$2"
+ shift
+ ;;
+ -t|--timeout)
+ timeout="$2"
+ shift
+ ;;
+ -o|--output)
+ output="$2"
+ shift
+ ;;
+ -w|--when)
+ when="$2"
+ shift
+ ;;
+ --compiler-filter)
+ compiler_filter="$2"
+ shift
+ ;;
+ *)
+ echo "Invalid argument: $1" >&2
+ exit 1
+ esac
+ shift
+ done
+
+ if [[ $when == "aot" ]]; then
+ # TODO: re-implement aot later for experimenting.
+ echo "Error: --when $when is unsupported" >&2
+ exit 1
+ elif [[ $when != "jit" ]]; then
+ echo "Error: --when must be one of (aot jit)." >&2
+ exit 1
+ fi
+}
+
+echo_to_output_file() {
+ if [[ "x$output" != x ]]; then
+ echo "$@" >> $output
+ fi
+ # Always echo to stdout as well.
+ echo "$@"
+}
+
+find_package_path() {
+ local pkg="$1"
+
+ res="$(adb shell find "/data/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
+ if [[ -z $res ]]; then
+ res="$(adb shell find "/system/app/$pkg"-'*' -maxdepth 0 2> /dev/null)"
+ fi
+ echo "$res"
+}
+
+# Main entry point
+if [[ $# -eq 0 ]]; then
+ usage
+ exit 1
+else
+ parse_arguments "$@"
+
+ # if we do not have have package exit early with an error
+ [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1
+
+ if [[ $mode != "cold" && $mode != "warm" ]]; then
+ needs_trace_file="y"
+ if [[ -z "$input_file" ]] || ! [[ -f $input_file ]]; then
+ echo "--input not specified" 1>&2
+ exit 1
+ fi
+ fi
+
+ if [[ "$activity" == "" ]]; then
+ activity="$(get_activity_name "$package")"
+ if [[ "$activity" == "" ]]; then
+ echo "Activity name could not be found, invalid package name?" 1>&2
+ exit 1
+ else
+ verbose_print "Activity name inferred: " "$activity"
+ fi
+ fi
+fi
+
+adb root > /dev/null
+
+if [[ ($when == jit) || ($when == aot) ]] && [[ "$(adb shell getenforce)" != "Permissive" ]]; then
+ echo "Disable selinux permissions and restart framework."
+ adb shell setenforce 0
+ adb shell stop
+ adb shell start
+ adb wait-for-device
+fi
+
+# TODO: set performance governor etc, preferrably only once
+# before every single app run.
+
+# Kill everything before running.
+remote_pkill "$package"
+sleep 1
+
+timings_array=()
+
+package_path="$(find_package_path "$package")"
+if [[ $? -ne 0 ]]; then
+ echo "Failed to detect package path for '$package'" >&2
+ exit 1
+fi
+verbose_print "Package was in path '$package_path'"
+
+application_trace_file_path="$package_path/TraceFile.pb"
+trace_file_directory="$package_path"
+if [[ $needs_trace_file == y ]]; then
+ # system server always passes down the package path in a hardcoded spot.
+ if [[ $when == "jit" ]]; then
+ if ! iorapd_compiler_install_trace_file "$package" "$activity" "$input_file"; then
+ echo "Error: Failed to install compiled TraceFile.pb for '$package/$activity'" >&2
+ exit 1
+ fi
+ keep_application_trace_file="y"
+ else
+ echo "TODO: --readahead=aot is non-functional and needs to be fixed." >&2
+ exit 1
+ # otherwise use a temporary directory to get normal non-jit behavior.
+ trace_file_directory="/data/local/tmp/prefetch/$package"
+ adb shell mkdir -p "$trace_file_directory"
+ verbose_print adb push "$input_file" "$trace_file_directory/TraceFile.pb"
+ adb push "$input_file" "$trace_file_directory/TraceFile.pb"
+ fi
+fi
+
+# Everything other than JIT: remove the trace file,
+# otherwise system server activity hints will kick in
+# and the new just-in-time app pre-warmup will happen.
+if [[ $keep_application_trace_file == "n" ]]; then
+ iorapd_compiler_purge_trace_file "$package" "$activity"
+fi
+
+# Perform AOT readahead/pinning/etc when an application is about to be launched.
+# For JIT readahead, we allow the system to handle it itself (this is a no-op).
+#
+# For warm, cold, etc modes which don't need readahead this is always a no-op.
+perform_aot() {
+ local the_when="$1" # user: aot, jit
+ local the_mode="$2" # warm, cold, fadvise, mlock, etc.
+
+ # iorapd readahead for jit+(mlock/fadvise)
+ if [[ $the_when == "jit" && $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+ iorapd_readahead_enable
+ return 0
+ fi
+
+ if [[ $the_when != "aot" ]]; then
+ # TODO: just in time implementation.. should probably use system server.
+ return 0
+ fi
+
+ # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
+ if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+
+ # TODO: add activity_hint_sender.exp
+ verbose_print "starting with package=$package package_path=$trace_file_directory"
+ coproc hint_sender_fd { $ANDROID_BUILD_TOP/system/iorap/src/sh/activity_hint_sender.exp "$package" "$trace_file_directory" "$the_mode"; }
+ hint_sender_pid=$!
+ verbose_print "Activity hint sender began"
+
+ notification_success="n"
+ while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
+ verbose_print "$hint_sender_output"
+ if [[ "$hint_sender_output" == "Press any key to send completed event..."* ]]; then
+ verbose_print "WE DID SEE NOTIFICATION SUCCESS."
+ notification_success='y'
+ # Give it some time to actually perform the readaheads.
+ sleep $sleep_time
+ break
+ fi
+ done
+
+ if [[ $notification_success == 'n' ]]; then
+ echo "[FATAL] Activity hint notification failed." 1>&2
+ exit 1
+ fi
+ fi
+}
+
+# Perform cleanup at the end of each loop iteration.
+perform_post_launch_cleanup() {
+ local the_when="$1" # user: aot, jit
+ local the_mode="$2" # warm, cold, fadvise, mlock, etc.
+ local logcat_timestamp="$3" # timestamp from before am start.
+ local res
+
+ if [[ $the_when != "aot" ]]; then
+ if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+ # Validate that readahead completes.
+ # If this fails for some reason, then this will also discard the timing of the run.
+ iorapd_readahead_wait_until_finished "$package" "$activity" "$logcat_timestamp" "$timeout"
+ res=$?
+
+ iorapd_readahead_disable
+
+ return $res
+ fi
+ # Don't need to do anything for warm or cold.
+ return 0
+ fi
+
+ # any non-warm/non-cold modes should use the iorap-activity-hint wrapper script.
+ if [[ $the_mode != 'warm' && $the_mode != 'cold' ]]; then
+ # Clean up the hint sender by telling it that the launch was completed,
+ # and to shutdown the watcher.
+ echo "Done\n" >&"${hint_sender_fd[1]}"
+
+ while read -r -u "${hint_sender_fd[0]}" hint_sender_output; do
+ verbose_print "$hint_sender_output"
+ done
+
+ wait $hint_sender_pid
+ fi
+}
+
+configure_compiler_filter() {
+ local the_compiler_filter="$1"
+ local the_package="$2"
+ local the_activity="$3"
+
+ if [[ -z $the_compiler_filter ]]; then
+ verbose_print "No --compiler-filter specified, don't need to force it."
+ return 0
+ fi
+
+ local current_compiler_filter_info="$("$DIR"/query_compiler_filter.py --package "$the_package")"
+ local res=$?
+ if [[ $res -ne 0 ]]; then
+ return $res
+ fi
+
+ local current_compiler_filter
+ local current_reason
+ local current_isa
+ read current_compiler_filter current_reason current_isa <<< "$current_compiler_filter_info"
+
+ verbose_print "Compiler Filter="$current_compiler_filter "Reason="$current_reason "Isa="$current_isa
+
+ # Don't trust reasons that aren't 'unknown' because that means we didn't manually force the compilation filter.
+ # (e.g. if any automatic system-triggered compilations are not unknown).
+ if [[ $current_reason != "unknown" ]] || [[ $current_compiler_filter != $the_compiler_filter ]]; then
+ verbose_print "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
+ "$DIR"/force_compiler_filter --compiler-filter "$the_compiler_filter" --package "$the_package" --activity "$the_activity"
+ res=$?
+ else
+ verbose_print "Queried compiler-filter matched requested compiler-filter, skip forcing."
+ res=0
+ fi
+
+ return $res
+}
+
+# Ensure the APK is currently compiled with whatever we passed in via --compiler-filter.
+# No-op if this option was not passed in.
+configure_compiler_filter "$compiler_filter" "$package" "$activity" || exit 1
+
+# convert 'a=b\nc=d\ne=f\n...' into 'b,d,f,...'
+parse_metrics_output_string() {
+ # single string with newlines in it.
+ local input="$1"
+
+ local metric_name
+ local metric_value
+ local rest
+
+ local all_metrics=()
+
+ # (n1=v1 n2=v2 n3=v3 ...)
+ readarray -t all_metrics <<< "$input"
+
+ local kv_pair=()
+ local i
+
+ for i in "${all_metrics[@]}"
+ do
+ verbose_print "parse_metrics_output: element '$i'"
+ # name=value
+
+ IFS='=' read -r metric_name metric_value rest <<< "$i"
+
+ verbose_print "parse_metrics_output: metric_value '$metric_value'"
+
+ # (value1 value2 value3 ...)
+ all_metrics+=(${metric_value})
+ done
+
+ # "value1,value2,value3,..."
+ join_by ',' "${all_metrics[@]}"
+}
+
+# convert 'a=b\nc=d\ne=f\n... into b,d,f,...'
+parse_metrics_output() {
+ local metric_name
+ local metric_value
+ local rest
+
+ local all_metrics=()
+
+ while IFS='=' read -r metric_name metric_value rest; do
+ verbose_print "metric: $metric_name, value: $metric_value; rest: $rest"
+ all_metrics+=($metric_value)
+ done
+
+ join_by ',' "${all_metrics[@]}"
+}
+
+# convert 'a=b\nc=d\ne=f\n... into b,d,f,...'
+parse_metrics_header() {
+ local metric_name
+ local metric_value
+ local rest
+
+ local all_metrics=()
+
+ while IFS='=' read -r metric_name metric_value rest; do
+ verbose_print "metric: $metric_name, value: $metric_value; rest: $rest"
+ all_metrics+=($metric_name)
+ done
+
+ join_by ',' "${all_metrics[@]}"
+}
+
+if [[ $report_fully_drawn == y ]]; then
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate --reportfullydrawn | parse_metrics_header)"
+else
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)"
+fi
+
+# TODO: This loop logic could probably be moved into app_startup_runner.py
+for ((i=0;i<count;++i)) do
+ verbose_print "=========================================="
+ verbose_print "==== ITERATION $i ===="
+ verbose_print "=========================================="
+ if [[ $mode != "warm" ]]; then
+ # The package must be killed **before** we drop caches, otherwise pages will stay resident.
+ verbose_print "Kill package for non-warm start."
+ remote_pkill "$package"
+ verbose_print "Drop caches for non-warm start."
+ # Drop all caches to get cold starts.
+ adb shell "echo 3 > /proc/sys/vm/drop_caches"
+ fi
+
+ perform_aot "$when" "$mode"
+
+ verbose_print "Running with timeout $timeout"
+
+ pre_launch_timestamp="$(logcat_save_timestamp)"
+
+ # TODO: multiple metrics output.
+
+if [[ $report_fully_drawn == y ]]; then
+ total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" --reportfullydrawn | parse_metrics_output)"
+else
+ total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" | parse_metrics_output)"
+fi
+
+ if [[ $? -ne 0 ]]; then
+ echo "WARNING: Skip bad result, try iteration again." >&2
+ ((i=i-1))
+ continue
+ fi
+
+ perform_post_launch_cleanup "$when" "$mode" "$pre_launch_timestamp"
+
+ if [[ $? -ne 0 ]]; then
+ echo "WARNING: Skip bad cleanup, try iteration again." >&2
+ ((i=i-1))
+ continue
+ fi
+
+ echo "Iteration $i. Total time was: $total_time"
+
+ timings_array+=("$total_time")
+done
+
+# drop the first result which is usually garbage.
+timings_array=("${timings_array[@]:1}")
+
+# Print the CSV header first.
+echo_to_output_file "$metrics_header"
+
+# Print out interactive/debugging timings and averages.
+# Other scripts should use the --output flag and parse the CSV.
+for tim in "${timings_array[@]}"; do
+ echo_to_output_file "$tim"
+done
+
+if [[ x$output != x ]]; then
+ echo " Saved results to '$output'"
+fi
+
+if [[ $needs_trace_file == y ]] ; then
+ iorapd_compiler_purge_trace_file "$package" "$activity"
+fi
+
+# Kill the process to ensure AM isn't keeping it around.
+remote_pkill "$package"
+
+exit 0
diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py
new file mode 100755
index 0000000..2f1eff2
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Runner of one test given a setting.
+
+Run app and gather the measurement in a certain configuration.
+Print the result to stdout.
+See --help for more details.
+
+Sample usage:
+ $> ./python run_app_with_prefetch.py -p com.android.settings -a
+ com.android.settings.Settings -r fadvise -i input
+
+"""
+
+import argparse
+import os
+import sys
+import time
+from typing import List, Tuple, Optional
+
+# local imports
+import lib.adb_utils as adb_utils
+from lib.app_runner import AppRunner, AppRunnerListener
+
+# global variables
+DIR = os.path.abspath(os.path.dirname(__file__))
+
+sys.path.append(os.path.dirname(DIR))
+import lib.print_utils as print_utils
+import lib.cmd_utils as cmd_utils
+import iorap.lib.iorapd_utils as iorapd_utils
+
+class PrefetchAppRunner(AppRunnerListener):
+ def __init__(self,
+ package: str,
+ activity: Optional[str],
+ readahead: str,
+ compiler_filter: Optional[str],
+ timeout: Optional[int],
+ simulate: bool,
+ debug: bool,
+ input:Optional[str],
+ **kwargs):
+ self.app_runner = AppRunner(package,
+ activity,
+ compiler_filter,
+ timeout,
+ simulate)
+ self.app_runner.add_callbacks(self)
+
+ self.simulate = simulate
+ self.readahead = readahead
+ self.debug = debug
+ self.input = input
+ print_utils.DEBUG = self.debug
+ cmd_utils.SIMULATE = self.simulate
+
+
+ def run(self) -> Optional[List[Tuple[str]]]:
+ """Runs an app.
+
+ Returns:
+ A list of (metric, value) tuples.
+ """
+ return self.app_runner.run()
+
+ def preprocess(self):
+ passed = self.validate_options()
+ if not passed:
+ return
+
+ # Sets up adb environment.
+ adb_utils.root()
+ adb_utils.disable_selinux()
+ time.sleep(1)
+
+ # Kill any existing process of this app
+ adb_utils.pkill(self.app_runner.package)
+
+ if self.readahead != 'warm':
+ print_utils.debug_print('Drop caches for non-warm start.')
+ # Drop all caches to get cold starts.
+ adb_utils.vm_drop_cache()
+
+ if self.readahead != 'warm' and self.readahead != 'cold':
+ iorapd_utils.enable_iorapd_readahead()
+
+ def postprocess(self, pre_launch_timestamp: str):
+ passed = self._perform_post_launch_cleanup(pre_launch_timestamp)
+ if not passed and not self.app_runner.simulate:
+ print_utils.error_print('Cannot perform post launch cleanup!')
+ return None
+
+ # Kill any existing process of this app
+ adb_utils.pkill(self.app_runner.package)
+
+ def _perform_post_launch_cleanup(self, logcat_timestamp: str) -> bool:
+ """Performs cleanup at the end of each loop iteration.
+
+ Returns:
+ A bool indicates whether the cleanup succeeds or not.
+ """
+ if self.readahead != 'warm' and self.readahead != 'cold':
+ passed = iorapd_utils.wait_for_iorapd_finish(self.app_runner.package,
+ self.app_runner.activity,
+ self.app_runner.timeout,
+ self.debug,
+ logcat_timestamp)
+
+ if not passed:
+ return passed
+
+ return iorapd_utils.disable_iorapd_readahead()
+
+ # Don't need to do anything for warm or cold.
+ return True
+
+ def metrics_selector(self, am_start_output: str,
+ pre_launch_timestamp: str) -> str:
+ """Parses the metric after app startup by reading from logcat in a blocking
+ manner until all metrics have been found".
+
+ Returns:
+ the total time and displayed time of app startup.
+ For example: "TotalTime=123\nDisplayedTime=121
+ """
+ total_time = AppRunner.parse_total_time(am_start_output)
+ displayed_time = adb_utils.blocking_wait_for_logcat_displayed_time(
+ pre_launch_timestamp, self.app_runner.package, self.app_runner.timeout)
+
+ return 'TotalTime={}\nDisplayedTime={}'.format(total_time, displayed_time)
+
+ def validate_options(self) -> bool:
+ """Validates the activity and trace file if needed.
+
+ Returns:
+ A bool indicates whether the activity is valid.
+ """
+ needs_trace_file = self.readahead != 'cold' and self.readahead != 'warm'
+ if needs_trace_file and (self.input is None or
+ not os.path.exists(self.input)):
+ print_utils.error_print('--input not specified!')
+ return False
+
+ # Install necessary trace file. This must be after the activity checking.
+ if needs_trace_file:
+ passed = iorapd_utils.iorapd_compiler_install_trace_file(
+ self.app_runner.package, self.app_runner.activity, self.input)
+ if not cmd_utils.SIMULATE and not passed:
+ print_utils.error_print('Failed to install compiled TraceFile.pb for '
+ '"{}/{}"'.
+ format(self.app_runner.package,
+ self.app_runner.activity))
+ return False
+
+ return True
+
+
+
+def parse_options(argv: List[str] = None):
+ """Parses command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(
+ description='Run an Android application once and measure startup time.'
+ )
+
+ required_named = parser.add_argument_group('required named arguments')
+ required_named.add_argument('-p', '--package', action='store', dest='package',
+ help='package of the application', required=True)
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the
+ # optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-a', '--activity', action='store',
+ dest='activity',
+ help='launch activity of the application')
+ optional_named.add_argument('-s', '--simulate', dest='simulate',
+ action='store_true',
+ help='simulate the process without executing '
+ 'any shell commands')
+ optional_named.add_argument('-d', '--debug', dest='debug',
+ action='store_true',
+ help='Add extra debugging output')
+ optional_named.add_argument('-i', '--input', action='store', dest='input',
+ help='perfetto trace file protobuf',
+ default='TraceFile.pb')
+ optional_named.add_argument('-r', '--readahead', action='store',
+ dest='readahead',
+ help='which readahead mode to use',
+ default='cold',
+ choices=('warm', 'cold', 'mlock', 'fadvise'))
+ optional_named.add_argument('-t', '--timeout', dest='timeout', action='store',
+ type=int,
+ help='Timeout after this many seconds when '
+ 'executing a single run.',
+ default=10)
+ optional_named.add_argument('--compiler-filter', dest='compiler_filter',
+ action='store',
+ help='Which compiler filter to use.',
+ default=None)
+
+ return parser.parse_args(argv)
+
+def main():
+ opts = parse_options()
+ runner = PrefetchAppRunner(**vars(opts))
+ result = runner.run()
+
+ if result is None:
+ return 1
+
+ print(result)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/app_startup/run_app_with_prefetch_test.py b/startop/scripts/app_startup/run_app_with_prefetch_test.py
new file mode 100644
index 0000000..8a588e4
--- /dev/null
+++ b/startop/scripts/app_startup/run_app_with_prefetch_test.py
@@ -0,0 +1,286 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+"""Unit tests for the run_app_with_prefetch_test.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./run_app_with_prefetch_test.py
+ $> pytest run_app_with_prefetch_test.py
+ $> python -m pytest run_app_with_prefetch_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+import io
+import os
+import shlex
+import sys
+import tempfile
+# global imports
+from contextlib import contextmanager
+
+# pip imports
+import pytest
+# local imports
+import run_app_with_prefetch as runner
+from mock import call, patch, Mock
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from app_startup.lib.app_runner import AppRunner
+#
+# Argument Parsing Helpers
+#
+
+@contextmanager
+def ignore_stdout_stderr():
+ """Ignore stdout/stderr output for duration of this context."""
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ sys.stdout = io.StringIO()
+ sys.stderr = io.StringIO()
+ try:
+ yield
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+
+@contextmanager
+def argparse_bad_argument(msg):
+ """Asserts that a SystemExit is raised when executing this context.
+
+ If the assertion fails, print the message 'msg'.
+ """
+ with pytest.raises(SystemExit, message=msg):
+ with ignore_stdout_stderr():
+ yield
+
+def assert_bad_argument(args, msg):
+ """Asserts that the command line arguments in 'args' are malformed.
+
+ Prints 'msg' if the assertion fails.
+ """
+ with argparse_bad_argument(msg):
+ parse_args(args)
+
+def parse_args(args):
+ """
+ :param args: command-line like arguments as a single string
+ :return: dictionary of parsed key/values
+ """
+ # "-a b -c d" => ['-a', 'b', '-c', 'd']
+ return vars(runner.parse_options(shlex.split(args)))
+
+def default_dict_for_parsed_args(**kwargs):
+ """Combines it with all of the "optional" parameters' default values."""
+ d = {
+ 'readahead': 'cold',
+ 'simulate': None,
+ 'simulate': False,
+ 'debug': False,
+ 'input': 'TraceFile.pb',
+ 'timeout': 10,
+ 'compiler_filter': None,
+ 'activity': None
+ }
+ d.update(kwargs)
+ return d
+
+def default_mock_dict_for_parsed_args(include_optional=True, **kwargs):
+ """Combines default dict with all optional parameters with some mock required
+ parameters.
+ """
+ d = {'package': 'com.fake.package'}
+ if include_optional:
+ d.update(default_dict_for_parsed_args())
+ d.update(kwargs)
+ return d
+
+def parse_optional_args(str):
+ """
+ Parses an argument string which already includes all the required arguments
+ in default_mock_dict_for_parsed_args.
+ """
+ req = '--package com.fake.package'
+ return parse_args('%s %s' % (req, str))
+
+def test_argparse():
+ # missing arguments
+ assert_bad_argument('', '-p are required')
+
+ # required arguments are parsed correctly
+ ad = default_dict_for_parsed_args # assert dict
+ assert parse_args('--package xyz') == ad(package='xyz')
+
+ assert parse_args('-p xyz') == ad(package='xyz')
+
+ assert parse_args('-p xyz -s') == ad(package='xyz', simulate=True)
+ assert parse_args('-p xyz --simulate') == ad(package='xyz', simulate=True)
+
+ # optional arguments are parsed correctly.
+ mad = default_mock_dict_for_parsed_args # mock assert dict
+ assert parse_optional_args('--input trace.pb') == mad(input='trace.pb')
+
+ assert parse_optional_args('--compiler-filter speed') == \
+ mad(compiler_filter='speed')
+
+ assert parse_optional_args('-d') == mad(debug=True)
+ assert parse_optional_args('--debug') == mad(debug=True)
+
+ assert parse_optional_args('--timeout 123') == mad(timeout=123)
+ assert parse_optional_args('-t 456') == mad(timeout=456)
+
+ assert parse_optional_args('-r warm') == mad(readahead='warm')
+ assert parse_optional_args('--readahead warm') == mad(readahead='warm')
+
+ assert parse_optional_args('-a act') == mad(activity='act')
+ assert parse_optional_args('--activity act') == mad(activity='act')
+
+def test_main():
+ args = '--package com.fake.package --activity act -s'
+ opts = runner.parse_options(shlex.split(args))
+ result = runner.PrefetchAppRunner(**vars(opts)).run()
+ assert result == [('TotalTime', '123')]
+
+def _mocked_run_shell_command(*args, **kwargs):
+ if args[0] == 'adb shell ps | grep "music" | awk \'{print $2;}\'':
+ return (True, '9999')
+ else:
+ return (True, '')
+
+def test_preprocess_no_cache_drop():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ prefetch_app_runner = runner.PrefetchAppRunner(package='music',
+ activity='MainActivity',
+ readahead='warm',
+ compiler_filter=None,
+ timeout=None,
+ simulate=False,
+ debug=False,
+ input=None)
+
+ prefetch_app_runner.preprocess()
+
+ calls = [call('adb root'),
+ call('adb shell "getenforce"'),
+ call('adb shell "setenforce 0"'),
+ call('adb shell "stop"'),
+ call('adb shell "start"'),
+ call('adb wait-for-device'),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_preprocess_with_cache_drop():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ prefetch_app_runner = runner.PrefetchAppRunner(package='music',
+ activity='MainActivity',
+ readahead='cold',
+ compiler_filter=None,
+ timeout=None,
+ simulate=False,
+ debug=False,
+ input=None)
+
+ prefetch_app_runner.preprocess()
+
+ calls = [call('adb root'),
+ call('adb shell "getenforce"'),
+ call('adb shell "setenforce 0"'),
+ call('adb shell "stop"'),
+ call('adb shell "start"'),
+ call('adb wait-for-device'),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"'),
+ call('adb shell "echo 3 > /proc/sys/vm/drop_caches"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+def test_preprocess_with_cache_drop_and_iorapd_enabled():
+ with patch('lib.cmd_utils.run_shell_command',
+ new_callable=Mock) as mock_run_shell_command:
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+
+ with tempfile.NamedTemporaryFile() as input:
+ prefetch_app_runner = runner.PrefetchAppRunner(package='music',
+ activity='MainActivity',
+ readahead='fadvise',
+ compiler_filter=None,
+ timeout=None,
+ simulate=False,
+ debug=False,
+ input=input.name)
+
+ prefetch_app_runner.preprocess()
+
+ calls = [call('adb root'),
+ call('adb shell "getenforce"'),
+ call('adb shell "setenforce 0"'),
+ call('adb shell "stop"'),
+ call('adb shell "start"'),
+ call('adb wait-for-device'),
+ call(
+ 'adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"'),
+ call('adb shell "echo 3 > /proc/sys/vm/drop_caches"'),
+ call('bash -c "source {}; iorapd_readahead_enable"'.
+ format(AppRunner.IORAP_COMMON_BASH_SCRIPT))]
+ mock_run_shell_command.assert_has_calls(calls)
+
+@patch('lib.adb_utils.blocking_wait_for_logcat_displayed_time')
+@patch('lib.cmd_utils.run_shell_command')
+def test_postprocess_with_launch_cleanup(
+ mock_run_shell_command,
+ mock_blocking_wait_for_logcat_displayed_time):
+ mock_run_shell_command.side_effect = _mocked_run_shell_command
+ mock_blocking_wait_for_logcat_displayed_time.return_value = 123
+
+ with tempfile.NamedTemporaryFile() as input:
+ prefetch_app_runner = runner.PrefetchAppRunner(package='music',
+ activity='MainActivity',
+ readahead='fadvise',
+ compiler_filter=None,
+ timeout=10,
+ simulate=False,
+ debug=False,
+ input=input.name)
+
+ prefetch_app_runner.postprocess('2019-07-02 23:20:06.972674825')
+
+ calls = [
+ call('bash -c "source {script_path}; '
+ 'iorapd_readahead_wait_until_finished '
+ '\'{package}\' \'{activity}\' \'{timestamp}\' \'{timeout}\'"'.
+ format(timeout=10,
+ package='music',
+ activity='MainActivity',
+ timestamp='2019-07-02 23:20:06.972674825',
+ script_path=AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call('bash -c "source {}; iorapd_readahead_disable"'.
+ format(AppRunner.IORAP_COMMON_BASH_SCRIPT)),
+ call('adb shell ps | grep "music" | awk \'{print $2;}\''),
+ call('adb shell "kill 9999"')]
+ mock_run_shell_command.assert_has_calls(calls)
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/app_startup/unlock_screen b/startop/scripts/app_startup/unlock_screen
new file mode 100755
index 0000000..478294c
--- /dev/null
+++ b/startop/scripts/app_startup/unlock_screen
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2018, 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.
+
+# This turns the screen on if it's off.
+# If it's on it does nothing unless its on the home screen, in which case it opens up some background
+# menu.
+#
+# However, this menu is ignored because "am start" commands still work as expected.
+adb shell input keyevent MENU
diff --git a/startop/scripts/iorap/analyze_prefetch_file.py b/startop/scripts/iorap/analyze_prefetch_file.py
new file mode 100755
index 0000000..343cd54
--- /dev/null
+++ b/startop/scripts/iorap/analyze_prefetch_file.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020, 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.
+
+import argparse
+import os
+import sys
+from typing import Dict, List, NamedTuple, Tuple
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR)) # framework/base/startop/script
+import lib.print_utils as print_utils
+
+# Include generated protos.
+dir_name = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(dir_name + "/generated")
+
+from TraceFile_pb2 import *
+
+def parse_options(argv: List[str] = None):
+ """Parses command line arguments and returns an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Analyze compiled_trace iorap protos.")
+ required_named = parser.add_argument_group('required named arguments')
+
+ required_named.add_argument('-i', dest='input', metavar='FILE',
+ help='Read protobuf file as input')
+
+ optional_named = parser.add_argument_group('optional named arguments')
+
+ optional_named.add_argument('-up', dest='upper_percent', type=float,
+ default=95.0,
+ help='Only show the top-most entries up to this value.')
+
+ optional_named.add_argument('-r', dest='raw', action='store_true',
+ help='Output entire raw file.')
+ optional_named.add_argument('-o', dest='output',
+ help='The results are stored into the output file')
+ optional_named.add_argument('-d', dest='debug', action='store_true'
+ , help='Activity of the app to be compiled')
+
+ return parser.parse_args(argv)
+
+def open_iorap_prefetch_file(file_path: str) -> TraceFile:
+ with open(file_path, "rb") as f:
+ tf = TraceFile()
+ tf.ParseFromString(f.read())
+ return tf
+
+def print_stats_summary(trace_file: TraceFile, upper_percent):
+ tf_dict = convert_to_dict(trace_file)
+ print_utils.debug_print(tf_dict)
+
+ total_length = 0
+ summaries = []
+ for name, entries_list in tf_dict.items():
+ summary = entries_sum(entries_list)
+ summaries.append(summary)
+
+ total_length += summary.length
+
+ # Sort by length
+ summaries.sort(reverse=True, key=lambda s: s.length)
+
+ percent_sum = 0.0
+ skipped_entries = 0
+
+ print("===========================================")
+ print("Total length: {:,} bytes".format(total_length))
+ print("Displayed upper percent: {:0.2f}%".format(upper_percent))
+ print("===========================================")
+ print("")
+ print("name,length,percent_of_total,upper_percent")
+ for sum in summaries:
+ percent_of_total = (sum.length * 1.0) / (total_length * 1.0) * 100.0
+
+ percent_sum += percent_of_total
+
+ if percent_sum > upper_percent:
+ skipped_entries = skipped_entries + 1
+ continue
+
+ #print("%s,%d,%.2f%%" %(sum.name, sum.length, percent_of_total))
+ print("{:s},{:d},{:0.2f}%,{:0.2f}%".format(sum.name, sum.length, percent_of_total, percent_sum))
+
+ if skipped_entries > 0:
+ print("[WARNING] Skipped {:d} entries, use -up=100 to show everything".format(skipped_entries))
+
+ pass
+
+class FileEntry(NamedTuple):
+ id: int
+ name: str
+ offset: int
+ length: int
+
+class FileEntrySummary(NamedTuple):
+ name: str
+ length: int
+
+def entries_sum(entries: List[FileEntry]) -> FileEntrySummary:
+ if not entries:
+ return None
+
+ summary = FileEntrySummary(name=entries[0].name, length=0)
+ for entry in entries:
+ summary = FileEntrySummary(summary.name, summary.length + entry.length)
+
+ return summary
+
+def convert_to_dict(trace_file: TraceFile) -> Dict[str, FileEntry]:
+ trace_file_index = trace_file.index
+
+ # entries.id -> entry.file_name
+ entries_map = {}
+
+ index_entries = trace_file_index.entries
+ for entry in index_entries:
+ entries_map[entry.id] = entry.file_name
+
+ final_map = {}
+
+ file_entries_map = {}
+ file_entries = trace_file.list.entries
+ for entry in file_entries:
+ print_utils.debug_print(entry)
+
+ lst = file_entries_map.get(entry.index_id, [])
+ file_entries_map[entry.index_id] = lst
+
+ file_name = entries_map[entry.index_id]
+ file_entry = \
+ FileEntry(id=entry.index_id, name=file_name, offset=entry.file_offset, length=entry.file_length)
+
+ lst.append(file_entry)
+
+ final_map[file_name] = lst
+
+ return final_map
+
+def main(argv: List[str]) -> int:
+ opts = parse_options(argv[1:])
+ if opts.debug:
+ print_utils.DEBUG = opts.debug
+ print_utils.debug_print(opts)
+
+ prefetch_file = open_iorap_prefetch_file(opts.input)
+
+ if opts.raw:
+ print(prefetch_file)
+
+ print_stats_summary(prefetch_file, opts.upper_percent)
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/startop/scripts/iorap/collector b/startop/scripts/iorap/collector
new file mode 100755
index 0000000..3dc080a
--- /dev/null
+++ b/startop/scripts/iorap/collector
@@ -0,0 +1,403 @@
+#!/bin/bash
+#
+# Copyright 2017, 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+APP_STARTUP_DIR="$DIR/../app_startup/"
+source "$DIR/common"
+
+usage() {
+ cat <<EOF
+Usage: collector [OPTIONS]...
+
+Runs an application, causes an iorap trace to be collected for it, and then invokes the iorap
+compiler to generate a TraceFile.pb.
+
+ -p, --package package of the app to test
+ -a, --activity activity of the app to test
+ -h, --help usage information (this)
+ -v, --verbose enable extra verbose printing
+ -i, --inodes path to inodes file (system/extras/pagecache/pagecache.py -d inodes)
+ -b, --trace_buffer_size how big to make trace buffer size (default 32768)
+ -w, --wait_time how long to run systrace for (default 10) in seconds
+ -c, --compiler-filter override the compilation filter if set (default none)
+ -o, --output output trace file protobuf (default 'TraceFile.pb')
+EOF
+}
+
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+trace_buffer_size=32768
+wait_time=10
+comp_filter=""
+output_dest="TraceFile.pb"
+
+parse_arguments() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -a|--activity)
+ activity="$2"
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -p|--package)
+ package="$2"
+ shift
+ ;;
+ -i|--inodes)
+ inodes="$2"
+ shift
+ ;;
+ -b|--trace_buffer_size)
+ trace_buffer_size="$2"
+ shift
+ ;;
+ -w|--wait_time)
+ wait_time="$2"
+ shift
+ ;;
+ -c|--compiler-filter)
+ comp_filter="$2"
+ shift
+ ;;
+ -o|--output)
+ output_dest="$2"
+ shift
+ ;;
+ -v|--verbose)
+ verbose="y"
+ ;;
+ esac
+ shift
+ done
+}
+
+remote_pidof() {
+ local procname="$1"
+ adb shell ps | grep "$procname" | awk '{print $2;}'
+}
+
+remote_pkill() {
+ local procname="$1"
+ shift
+
+ local the_pids=$(remote_pidof "$procname")
+ local pid
+
+ for pid in $the_pids; do
+ verbose_print adb shell kill "$@" "$pid"
+ adb shell kill "$@" "$pid"
+ done
+}
+
+force_package_compilation() {
+ local arg_comp_filter="$1"
+ local arg_package="$2"
+
+ if [[ $arg_comp_filter == speed-profile ]]; then
+ # Force the running app to dump its profiles to disk.
+ remote_pkill "$arg_package" -SIGUSR1
+ sleep 1 # give some time for above to complete.
+ fi
+
+ adb shell cmd package compile -m "$arg_comp_filter" -f "$arg_package"
+}
+
+parse_package_dumpsys_line() {
+ local what_left="$1"
+ local what_right="$2"
+ local line="$3"
+
+ if [[ $line == *${what_left}*${what_right}* ]]; then
+ found="${line#*$what_left}"
+ found="${found%$what_right*}"
+ echo "$found"
+ return 0
+ fi
+
+ return 1
+}
+
+parse_package_dumpsys_section() {
+ local what_left="$1"
+ local what_right="$2"
+ shift
+ local lines="$@"
+
+ lines="${lines//$'\n'/}"
+
+ local new_lines=()
+
+ local current_line=""
+ local newline=n
+ local line
+ for line in "${lines[@]}"; do
+ if [[ $line == *: ]]; then
+ newline=y
+ current_line=""
+ new_lines+=("$current_line")
+
+ parse_package_dumpsys_line "$what_left" "$what_right" "$current_line" && return 0
+ else
+ # strip all spaces from the start
+ line="${line//$' '/}"
+ current_line+="$line"
+ #prepend to current line
+ fi
+ done
+ [[ "$current_line" != "" ]] && new_lines+=("$current_line")
+
+ parse_package_dumpsys_line "$what_left" "$what_right" "$current_line" && return 0
+
+ return 1
+}
+
+parse_package_compilation() {
+ local pkg="$1"
+# [com.google.android.apps.maps]
+
+ local compilation_filter
+ local is_prebuilt
+ local isa
+ local etc
+
+ local ret_code
+
+ read compilation_filter is_prebuilt isa etc <<< "$("$APP_STARTUP_DIR"/query_compiler_filter.py --package "$pkg")"
+ ret_code=$?
+
+ if [[ $ret_code -eq 0 && x$compilation_filter != x ]]; then
+ verbose_print "Package compilation info for $pkg was '$compilation_filter'"
+ echo "$compilation_filter"
+ return 0
+ else
+ verbose_print "query failed ret code $ret_code filter=$compilation_filter"
+ fi
+
+ return $ret_code
+}
+
+# Main entry point
+if [[ $# -eq 0 ]]; then
+ usage
+ exit 1
+else
+ parse_arguments "$@"
+
+ # if we do not have have package exit early with an error
+ [[ "$package" == "" ]] && echo "--package not specified" 1>&2 && exit 1
+
+ if [[ -z "$inodes" ]] || ! [[ -f $inodes ]]; then
+ echo "--inodes not specified" 1>&2
+ exit 1
+ fi
+
+ if [[ "$activity" == "" ]]; then
+ activity="$(get_activity_name "$package")"
+ if [[ "$activity" == "" ]]; then
+ echo "Activity name could not be found, invalid package name?" 1>&2
+ exit 1
+ else
+ verbose_print "Activity name inferred: " "$activity"
+ fi
+ fi
+fi
+
+adb root > /dev/null
+
+if [[ "$(adb shell getenforce)" != "Permissive" ]]; then
+ adb shell setenforce 0
+ adb shell stop
+ adb shell start
+ adb wait-for-device
+fi
+
+compilation_was="$(parse_package_compilation "$package")"
+if [[ $? -ne 0 ]]; then
+ echo "Could not determine package compilation filter; was this package installed?" >&2
+ exit 1
+fi
+verbose_print "Package compilation: $compilation_was"
+
+# Cannot downgrade (e.g. from speed-profile to quicken) without forceful recompilation.
+# Forceful recompilation will recompile even if compilation filter was unchanged.
+# Therefore avoid recompiling unless the filter is actually different than what we asked for.
+if [[ "x$comp_filter" != "x" ]] && [[ "$compilation_was" != "$comp_filter" ]]; then
+ echo "Current compilation filter is '$compilation_was'; force recompile to '$comp_filter'" >&2
+ #TODO: this matching seems hopelessly broken, it will always recompile.
+
+ force_package_compilation "$comp_filter" "$package"
+fi
+
+# Drop all caches prior to beginning a systrace, otherwise we won't record anything already in pagecache.
+adb shell "echo 3 > /proc/sys/vm/drop_caches"
+
+trace_tmp_file="$(mktemp -t trace.XXXXXXXXX.html)"
+
+function finish {
+ [[ -f "$trace_tmp_file" ]] && rm "$trace_tmp_file"
+}
+trap finish EXIT
+
+launch_application_and_wait_for_trace() {
+ local package="$1"
+ local activity="$2"
+ local timeout=30 # seconds
+
+ # Ensure application isn't running already.
+ remote_pkill "$package"
+
+ # 5 second trace of Home screen causes
+ # a trace of the home screen.
+ # There is no way to abort the trace
+ # so just wait for it to complete instead.
+ sleep 30
+
+ local time_now="$(logcat_save_timestamp)"
+ local retcode=0
+
+ verbose_print "Drop caches for non-warm start."
+ # Drop all caches to get cold starts.
+ adb shell "echo 3 > /proc/sys/vm/drop_caches"
+
+ verbose_print "now launching application"
+ # Launch an application
+ "$APP_STARTUP_DIR"/launch_application "$package" "$activity"
+ retcode=$?
+ if [[ $retcode -ne 0 ]]; then
+ echo "FATAL: Application launch failed." >&2
+ return $retcode
+ fi
+
+ # This blocks until 'am start' returns at which point the application is
+ # already to be considered "started" as the first frame has been drawn.
+
+ # TODO: check for cold start w.r.t to activitymanager?
+
+ # Wait for application to start from the point of view of ActivityTaskManager.
+ local pattern="ActivityTaskManager: Displayed $package"
+ logcat_wait_for_pattern "$timeout" "$time_now" "$pattern"
+ retcode=$?
+ if [[ $retcode -ne 0 ]]; then
+ echo "FATAL: Could not find '$pattern' in logcat." >&2
+ return $retcode
+ fi
+
+ # Wait for iorapd to finish writing out the perfetto traces for this app.
+ iorapd_perfetto_wait_for_app_trace "$package" "$activity" "$timeout" "$time_now"
+ retcode=$?
+ if [[ $retcode -ne 0 ]]; then
+ echo "FATAL: Could not save perfetto app trace file." >&2
+ return $retcode
+ fi
+
+ verbose_print "iorapd has finished collecting app trace file for $package/$activity"
+}
+
+collector_main() {
+ # don't even bother trying to run anything until the screen is unlocked.
+ "$APP_STARTUP_DIR"/unlock_screen
+
+ # Don't mutate state while iorapd is running.
+ iorapd_stop || return $?
+
+ # Remove all existing metadata for a package/activity in iorapd.
+ iorapd_perfetto_purge_app_trace "$package" "$activity" || return $?
+ iorapd_compiler_purge_trace_file "$package" "$activity" || return $?
+
+ iorapd_perfetto_enable || return $?
+ iorapd_readahead_disable || return $?
+ iorapd_start || return $?
+
+ # Wait for perfetto trace to finished writing itself out.
+ launch_application_and_wait_for_trace "$package" "$activity" || return $?
+
+ # Pull the perfetto trace for manual inspection.
+ iorapd_perfetto_pull_trace_file "$package" "$activity" "perfetto_trace.pb"
+
+ # Compile the trace so that the next app run can use prefetching.
+ iorapd_compiler_for_app_trace "$package" "$activity" "$inodes" || return $?
+
+ # Save TraceFile.pb to local file.
+ iorapd_compiler_pull_trace_file "$package" "$activity" "$output_dest" || return $?
+ # Remove the TraceFile.pb from the device.
+ iorapd_compiler_purge_trace_file "$package" "$activity" || return $?
+
+ # TODO: better transactional support for restoring iorapd global properties
+ iorapd_perfetto_disable || return $?
+}
+
+collector_main "$@"
+
+verbose_print "Collector finished. Children: "
+if [[ $verbose == y ]]; then
+ jobs -p
+ ps f -g$$
+fi
+
+exit $?
+
+
+verbose_print "About to begin systrace"
+coproc systrace_fd {
+ # Disable stdout buffering since we need to know the output of systrace RIGHT AWAY.
+ stdbuf -oL "$ANDROID_BUILD_TOP"/external/chromium-trace/systrace.py --target=android -b "$trace_buffer_size" -t "$wait_time" am pagecache dalvik -o "$trace_tmp_file"
+}
+
+verbose_print "Systrace began"
+
+systrace_pid="$!"
+
+while read -r -u "${systrace_fd[0]}" systrace_output; do
+ verbose_print "$systrace_output"
+ if [[ "$systrace_output" == *"Starting tracing"* ]]; then
+ verbose_print "WE DID SEE STARTING TRACING."
+ break
+ fi
+done
+# Systrace has begun recording the tracing.
+# Run the application and collect the results.
+
+am_output="$(adb shell am start -S -W "$package"/"$activity")"
+if [[ $? -ne 0 ]]; then
+ echo "am start failed" >&2
+
+ exit 1
+fi
+
+verbose_print "$am_output"
+total_time="$(echo "$am_output" | grep 'TotalTime:' | sed 's/TotalTime: //g')"
+verbose_print "total time: $total_time"
+
+# Now wait for systrace to finish.
+
+wait "$systrace_pid" || { echo "Systrace finished before am start was finished, try a longer --wait_time"; exit 1; }
+verbose_print "Systrace has now finished"
+verbose_print "$(ls -la "$trace_tmp_file")"
+
+
+iorapd_perfetto_disable
+
+# Now that systrace has finished, convert the trace file html file to a protobuf.
+
+"$ANDROID_BUILD_TOP"/system/iorap/src/py/collector/trace_parser.py -i "$inodes" -t "$trace_tmp_file" -o "$output_dest" || exit 1
+
+echo "Trace file collection complete, trace file saved to \"$output_dest\"!" >&2
+
+finish
diff --git a/startop/scripts/iorap/common b/startop/scripts/iorap/common
new file mode 100755
index 0000000..387e45d
--- /dev/null
+++ b/startop/scripts/iorap/common
@@ -0,0 +1,253 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+DIR_IORAP_COMMON="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+APP_STARTUP_DIR="$DIR_IORAP_COMMON/../app_startup/"
+source "$APP_STARTUP_DIR/lib/common"
+
+IORAPD_DATA_PATH="/data/misc/iorapd"
+
+iorapd_start() {
+ verbose_print 'iorapd_start'
+ adb shell start iorapd
+ sleep 1
+ # TODO: block until logcat prints successfully connecting
+}
+
+iorapd_stop() {
+ verbose_print 'iorapd_stop'
+ adb shell stop iorapd
+}
+
+iorapd_reset() {
+ iorapd_stop
+ iorapd_start
+}
+
+# Enable perfetto tracing.
+# Subsequent launches of an application will record a perfetto trace protobuf.
+iorapd_perfetto_enable() {
+ verbose_print 'enable perfetto'
+ adb shell setprop iorapd.perfetto.enable true
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
+# Disable perfetto tracing.
+# Subsequent launches of applications will no longer record perfetto trace protobufs.
+iorapd_perfetto_disable() {
+ verbose_print 'disable perfetto'
+ adb shell setprop iorapd.perfetto.enable false
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
+# Enable readahead
+# Subsequent launches of an application will be sped up by iorapd readahead prefetching
+# (Provided an appropriate compiled trace exists for that application)
+iorapd_readahead_enable() {
+ if [[ "$(adb shell getprop iorapd.readahead.enable)" == true ]]; then
+ verbose_print 'enable readahead [already enabled]'
+ return 0
+ fi
+ verbose_print 'enable readahead [reset iorapd]'
+ adb shell setprop iorapd.readahead.enable true
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
+# Disable readahead
+# Subsequent launches of an application will be not be sped up by iorapd readahead prefetching.
+iorapd_readahead_disable() {
+ if [[ "$(adb shell getprop iorapd.readahead.enable)" == false ]]; then
+ verbose_print 'disable readahead [already disabled]'
+ return 0
+ fi
+ verbose_print 'disable readahead [reset iorapd]'
+ adb shell setprop iorapd.readahead.enable false
+ iorapd_reset # iorapd only reads this flag when initializing
+}
+
+_iorapd_path_to_data_file() {
+ local package="$1"
+ local activity="$2"
+ local suffix="$3"
+
+ # Match logic of 'AppComponentName' in iorap::compiler C++ code.
+ echo "${IORAPD_DATA_PATH}/${package}%2F${activity}.${suffix}"
+}
+
+iorapd_perfetto_wait_for_app_trace() {
+ local package="$1"
+ local activity="$2"
+ local timeout="$3"
+ local timestamp="$4"
+
+ local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "perfetto_trace.pb")"
+
+ verbose_print "iorapd_perfetto_wait_for_app_trace on file '$remote_path'"
+
+ # see event_manager.cc
+ local pattern="Perfetto TraceBuffer saved to file: $remote_path"
+ logcat_wait_for_pattern "$timeout" "$timestamp" "$pattern"
+}
+
+# Purge all perfetto traces for a given application.
+iorapd_perfetto_purge_app_trace() {
+ local package="$1"
+ local activity="$2"
+
+ local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "perfetto_trace.pb")"
+
+ verbose_print 'iorapd-perfetto: purge app trace in ' "$remote_path"
+ adb shell "[[ -f '$remote_path' ]] && rm -f '$remote_path' || exit 0"
+}
+
+# Pull the remote perfetto trace file into a local file.
+iorapd_perfetto_pull_trace_file() {
+ local package="$1"
+ local activity="$2"
+ local output_file="$3" # local path
+
+ local compiled_path="$(_iorapd_path_to_data_file "$package" "$activity" "perfetto_trace.pb")"
+
+ if ! adb shell "[[ -f '$compiled_path' ]]"; then
+ echo "Error: Remote path '$compiled_path' invalid" >&2
+ return 1
+ fi
+ if ! mkdir -p "$(dirname "$output_file")"; then
+ echo "Error: Fail to make output directory for '$output_file'" >&2
+ return 1
+ fi
+ verbose_print adb pull "$compiled_path" "$output_file"
+ adb pull "$compiled_path" "$output_file"
+}
+
+# Compile a perfetto trace for a given application.
+# This requires the app has run at least once with perfetto tracing enabled.
+iorapd_compiler_for_app_trace() {
+ local package="$1"
+ local activity="$2"
+ local inodes="$3" # local path
+
+ # remote path calculations
+ local input_path="$(_iorapd_path_to_data_file "$package" "$activity" "perfetto_trace.pb")"
+ local compiled_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.tmp.pb")"
+ local compiled_path_final="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
+
+ if ! adb shell "[[ -f '$input_path' ]]"; then
+ echo "Error: Missing perfetto traces; nothing to compile. Expected: '$input_path'" >&2
+ return 1
+ fi
+
+ if ! [[ -f $inodes ]]; then
+ # We could compile using 'diskscan' but it's non-deterministic, so refuse instead.
+ echo "Error: Missing inodes textcache at '$inodes'; refusing to compile." >&2
+ return 1
+ fi
+
+ # inodes file needs to be on the device for iorap.cmd.compiler to access it
+ local remote_inodes=/data/local/tmp/prefetch/inodes.txt
+ adb shell "mkdir -p \"$(dirname "$remote_inodes")\"" || return 1
+ verbose_print adb push "$inodes" "$remote_inodes"
+ adb push "$inodes" "$remote_inodes"
+
+ verbose_print 'iorapd-compiler: compile app trace in ' "$input_path"
+ verbose_print adb shell "iorap.cmd.compiler '$input_path' --inode-textcache '$remote_inodes' --output-proto '$compiled_path'"
+ adb shell "iorap.cmd.compiler '$input_path' --inode-textcache '$remote_inodes' --output-proto '$compiled_path'"
+ retcode=$?
+
+ # Don't overwrite the true 'compiled_trace.pb' unless the compiler completed without error.
+ # TODO: The native compiler code should be handling its own transaction-safety.
+ if [[ $retcode -eq 0 ]]; then
+ adb shell "mv '$compiled_path' '$compiled_path_final'"
+ else
+ adb shell "[[ -f '$compiled_path' ]] && rm -f '$compiled_path'"
+ fi
+
+ # Clean up inodes file we just pushed.
+# adb shell "[[ -f '$remote_inodes' ]] && rm -f '$remote_inodes'"
+
+ return $retcode
+}
+
+# Pull the remote compiled trace file into a local file.
+iorapd_compiler_pull_trace_file() {
+ local package="$1"
+ local activity="$2"
+ local output_file="$3" # local path
+
+ local compiled_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
+
+ if ! adb shell "[[ -f '$compiled_path' ]]"; then
+ echo "Error: Remote path '$compiled_path' invalid" >&2
+ return 1
+ fi
+ if ! mkdir -p "$(dirname "$output_file")"; then
+ echo "Error: Fail to make output directory for '$output_file'" >&2
+ return 1
+ fi
+ verbose_print adb pull "$compiled_path" "$output_file"
+ adb pull "$compiled_path" "$output_file"
+}
+
+# Install a compiled trace file.
+iorapd_compiler_install_trace_file() {
+ local package="$1"
+ local activity="$2"
+ local input_file="$3" # local path
+
+ # remote path calculations
+ local compiled_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
+
+ if ! [[ -f $input_file ]]; then
+ echo "Error: File '$input_file' does not exist." >&2
+ return 1
+ fi
+
+ adb shell "mkdir -p \"$(dirname "$compiled_path")\"" || return 1
+
+ verbose_print adb push "$input_file" "$compiled_path"
+ adb push "$input_file" "$compiled_path"
+}
+
+iorapd_compiler_purge_trace_file() {
+ local package="$1"
+ local activity="$2"
+ local input_file="$3" # local path
+
+ local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
+
+ adb shell "[[ -f '$remote_path' ]] && rm -f '$remote_path' || exit 0"
+}
+
+# Blocks until the readahead for the requested package/activity has finished.
+# This assumes that the trace file was already installed, and also that
+# the application launched but not completed yet.
+iorapd_readahead_wait_until_finished() {
+ local package="$1"
+ local activity="$2"
+ local timestamp="$3"
+ local timeout="$4"
+
+ if [[ $# -lt 4 ]]; then
+ echo "FATAL: Expected 4 arguments (actual $# $@)" >&2
+ exit 1
+ fi
+
+ local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
+
+ # See 'read_ahead.cc' LOG(INFO).
+ local pattern="Description = $remote_path"
+ logcat_wait_for_pattern "$timeout" "$timestamp" "$pattern"
+}
diff --git a/startop/scripts/iorap/compile_handcrafted_file.py b/startop/scripts/iorap/compile_handcrafted_file.py
new file mode 100755
index 0000000..6dbbeaf
--- /dev/null
+++ b/startop/scripts/iorap/compile_handcrafted_file.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+import argparse
+import asyncio
+import csv
+import itertools
+import os
+import re
+import struct
+import sys
+import tempfile
+import time
+import zipfile
+from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union
+
+# Include generated protos.
+dir_name = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(dir_name + "/generated")
+
+from TraceFile_pb2 import *
+
+
+def parse_options(argv: List[str] = None):
+ """Parse command line arguments and return an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Compile a TraceFile.proto from a manual text file.")
+ # argparse considers args starting with - and -- optional in --help, even though required=True.
+ # by using a named argument group --help will clearly say that it's required instead of optional.
+ required_named = parser.add_argument_group('required named arguments')
+
+ # optional arguments
+ # use a group here to get the required arguments to appear 'above' the optional arguments in help.
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-opb', '--output-proto-binary', dest='output_proto_binary', action='store', help='Write binary proto output to file.')
+ optional_named.add_argument('-pm', '--pinlist-meta', dest='pinlist_meta', action='store', help='Path to pinlist.meta (default=none) binary file.')
+ optional_named.add_argument('-pmp', '--pinlist-meta-parent', dest='pinlist_meta_parent', action='store', help='Device path that the pinlist.meta applies to (e.g. /data/.../somefile.apk)')
+ optional_named.add_argument('-i', '--input', dest='input', action='store', help='Input text file (default stdin).')
+ optional_named.add_argument('-zp', '--zip_path', dest='zip_path', action='append', help='Directory containing zip files.')
+ optional_named.add_argument('-d', '--debug', dest='debug', action='store_true', help='Add extra debugging output')
+ optional_named.add_argument('-ot', '--output-text', dest='output_text', action='store', help='Output text file (default stdout).')
+
+ return parser.parse_args(argv)
+
+# TODO: refactor this with a common library file with analyze_metrics.py
+def _debug_print(*args, **kwargs):
+ """Print the args to sys.stderr if the --debug/-d flag was passed in."""
+ if _debug:
+ print(*args, **kwargs, file=sys.stderr)
+
+class BadInputError(Exception):
+ pass
+
+InputRecord = NamedTuple('InputRecord', [('filepath', str), ('offset', int), ('length', int), ('remark', str)])
+
+def find_zip_in_paths(original_name, zip_paths):
+ # /foo/bar/bax.zip -> bax.zip
+ file_basename = os.path.split(original_name)[1]
+
+ # the file must be located in one of the --zip-path arguments
+ matched = None
+ for zip_path in zip_paths:
+ for dir_entry in os.listdir(zip_path):
+ if dir_entry == file_basename:
+ matched = os.path.join(zip_path, dir_entry)
+ break
+ if matched:
+ break
+
+ if not matched:
+ raise ValueError("%s could not be found in any of the --zip_path specified." %(file_basename))
+
+ _debug_print("found zip file ", file_basename, " in ", matched)
+
+ if not zipfile.is_zipfile(matched):
+ raise ValueError("%s is not a zip file" %(matched))
+
+ return matched
+
+def handle_zip_entry(input_record, zip_paths):
+
+ res = re.match("([^!]+)[!](.*)", input_record.filepath)
+
+ if not res:
+ return input_record
+
+ # 'foo!bar'
+ in_filepath = res[1] # -> 'foo'
+ in_zip_entry = res[2] # -> 'bar'
+
+ matched = find_zip_in_paths(in_filepath, zip_paths)
+
+ zip = zipfile.ZipFile(matched)
+
+ try:
+ zip_info = zip.getinfo(in_zip_entry)
+ except KeyError:
+ raise ValueError("%s is not an item in the zip file %s" %(in_zip_entry, matched))
+
+ # TODO: do we also need to add header size to this?
+ in_offset = zip_info.header_offset
+
+ # TODO: if a range is specified, use that instead.
+ in_length = zip_info.compress_size
+
+ return InputRecord(in_filepath, in_offset, in_length, 'zip entry (%s)' %(in_zip_entry))
+
+def parse_input_file(input: Iterable[str], zip_paths: List[str]) -> Iterable[InputRecord]:
+ for line in input:
+ line = line.strip()
+
+ _debug_print("Line = ", line)
+ if not line:
+ _debug_print(" skip empty line", line)
+ continue
+ elif line[0] == "#":
+ _debug_print(" skip commented line", line)
+ continue
+
+ res = re.match("([^\s]+)\s+(\d+)\s+(\d+)", line)
+ if not res:
+ raise BadInputError("Expected input of form: <str:filepath> <int:offset> <int:length>")
+
+ in_filepath = res[1]
+ in_offset = int(res[2])
+ in_length = int(res[3])
+
+ yield handle_zip_entry(InputRecord(in_filepath, in_offset, in_length, 'regular file'), zip_paths)
+
+# format:
+# (<big_endian(i32):file_offset> <big_endian(i32):range_length>)+
+PIN_META_FORMAT = ">ii"
+PIN_META_READ_SIZE = struct.calcsize(PIN_META_FORMAT)
+
+def parse_pin_meta(pin_meta_file, pinlist_meta_parent, zip_paths):
+ if not pin_meta_file:
+ return ()
+
+ global PIN_META_FORMAT
+ global PIN_META_READ_SIZE
+
+ # '/data/app/com.google.android.GoogleCamera-aNQhzSznf4h_bvJ_MRbweQ==/base.apk'
+ # -> 'com.google.android.GoogleCamera'
+ package_name_match = re.match('/.*/(.*)-.*=/base.apk', pinlist_meta_parent)
+
+ if not package_name_match:
+ raise ValueError("%s did not contain the <packagename>.apk" %(pinlist_meta_parent))
+
+ package_name = package_name_match[1]
+ # "com.google.android.GoogleCamera" -> "GoogleCamera.apk"
+ apk_name = package_name.split(".")[-1] + ".apk"
+
+ path_to_zip_on_host = find_zip_in_paths(apk_name, zip_paths)
+ apk_file_size = os.path.getsize(path_to_zip_on_host)
+ _debug_print("APK path '%s' file size '%d'" %(path_to_zip_on_host, apk_file_size))
+
+ while True:
+ data = pin_meta_file.read(PIN_META_READ_SIZE)
+
+ if not data:
+ break
+
+ (pin_offset, pin_length) = struct.unpack(PIN_META_FORMAT, data) # (offset, length)
+
+ remark = 'regular file (pinlist.meta)'
+
+ remaining_size = apk_file_size - pin_offset
+ if remaining_size < 0:
+ print("WARNING: Clamp entry (%d, %d), offset too large (max file size = %d)" %(pin_offset, pin_length, apk_file_size))
+
+ pin_length = pin_length + remaining_size
+ pin_offset = pin_offset + remaining_size
+
+ if pin_offset < 0:
+ pin_offset = 0
+
+ remark += '[clamped.offset]'
+
+ pin_last_offset = pin_offset + pin_length
+ remaining_size = apk_file_size - pin_last_offset
+
+ if remaining_size < 0:
+ print("WARNING: Clamp entry (%d, %d), length too large (max file size = %d)" %(pin_offset, pin_length, apk_file_size))
+ pin_length = pin_length + remaining_size
+
+ remark += '[clamped.length]'
+
+ yield InputRecord(pinlist_meta_parent, pin_offset, pin_length, remark)
+
+def write_text_file_output(input_records: Iterable[InputRecord], output_text_file):
+ for rec in input_records:
+ output_text_file.write("%s %d %d #%s\n" %(rec.filepath, rec.offset, rec.length, rec.remark))
+
+def build_trace_file(input_records: Iterable[InputRecord]) -> TraceFile:
+ trace_file = TraceFile()
+ trace_file_index = trace_file.index
+
+ file_id_counter = 0
+ file_id_map = {} # filename -> id
+
+ stats_length_total = 0
+ filename_stats = {} # filename -> total size
+
+ for rec in input_records:
+ filename = rec.filepath
+
+ file_id = file_id_map.get(filename)
+ if not file_id:
+ file_id = file_id_counter
+ file_id_map[filename] = file_id_counter
+ file_id_counter = file_id_counter + 1
+
+ file_index_entry = trace_file_index.entries.add()
+ file_index_entry.id = file_id
+ file_index_entry.file_name = filename
+
+ # already in the file index, add the file entry.
+ file_entry = trace_file.list.entries.add()
+ file_entry.index_id = file_id
+ file_entry.file_length = rec.length
+ stats_length_total += file_entry.file_length
+ file_entry.file_offset = rec.offset
+
+ filename_stats[filename] = filename_stats.get(filename, 0) + file_entry.file_length
+
+ return trace_file
+
+def main():
+ global _debug
+
+ options= parse_options()
+ _debug = options.debug
+ _debug_print("parsed options: ", options)
+
+ if not options.input:
+ input_file = sys.stdin
+ _debug_print("input = stdin")
+ else:
+ input_file = open(options.input)
+ _debug_print("input = (file)", options.input)
+
+ if not options.output_proto_binary:
+ output_proto_file = None
+ else:
+ output_proto_file = open(options.output_proto_binary, 'wb')
+ _debug_print("output_proto_binary = ", output_proto_file)
+
+ pinlist_meta_parent = options.pinlist_meta_parent
+ if options.pinlist_meta:
+ pin_meta_file = open(options.pinlist_meta, 'rb')
+ else:
+ pin_meta_file = None
+
+ if (pinlist_meta_parent == None) != (pin_meta_file == None):
+ print("Options must be used together: --pinlist-meta and --pinlist-meta-path")
+ return 1
+
+ if not options.output_text:
+ output_text_file = sys.stdout
+ _debug_print("output = stdout")
+ else:
+ output_text_file = open(options.output_text, 'w')
+ _debug_print("output = (file)", options.output_text)
+
+ zip_paths = options.zip_path or []
+
+ input_records = list(parse_pin_meta(pin_meta_file, pinlist_meta_parent, zip_paths))
+ input_records = input_records + list(parse_input_file(input_file, zip_paths))
+
+ for p in input_records:
+ _debug_print(p)
+
+ write_text_file_output(input_records, output_text_file)
+ output_text_file.close()
+
+ out_proto = build_trace_file(input_records)
+
+ if output_proto_file:
+ output_proto_file.write(out_proto.SerializeToString())
+ output_proto_file.close()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
new file mode 100644
index 0000000..1426d34
--- /dev/null
+++ b/startop/scripts/iorap/compiler.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+import importlib
+import os
+import sys
+import tempfile
+from enum import Enum
+from typing import TextIO, List
+
+# local import
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+import lib.print_utils as print_utils
+
+# Type of compiler.
+class CompilerType(Enum):
+ HOST = 1 # iorap.cmd.compiler on host
+ DEVICE = 2 # adb shell iorap.cmd.compiler
+ RI = 3 # compiler.py
+
+def compile_perfetto_trace_ri(
+ argv: List[str],
+ compiler) -> TextIO:
+ print_utils.debug_print('Compile using RI compiler.')
+ compiler_trace_file = tempfile.NamedTemporaryFile()
+ argv.extend(['-o', compiler_trace_file.name])
+ print_utils.debug_print(argv)
+ compiler.main([''] + argv)
+ return compiler_trace_file
+
+def compile_perfetto_trace_device(inodes_path: str,
+ package: str,
+ activity: str,
+ compiler) -> TextIO:
+ print_utils.debug_print('Compile using on-device compiler.')
+ compiler_trace_file = tempfile.NamedTemporaryFile()
+ compiler.main(inodes_path, package, activity, compiler_trace_file.name)
+ return compiler_trace_file
+
+def compile(compiler_type: CompilerType,
+ inodes_path: str,
+ ri_compiler_argv,
+ package: str,
+ activity: str) -> TextIO:
+ if compiler_type == CompilerType.RI:
+ compiler = importlib.import_module('iorap.compiler_ri')
+ compiler_trace_file = compile_perfetto_trace_ri(ri_compiler_argv,
+ compiler)
+ return compiler_trace_file
+ if compiler_type == CompilerType.DEVICE:
+ compiler = importlib.import_module('iorap.compiler_device')
+ compiler_trace_file = compile_perfetto_trace_device(inodes_path,
+ package,
+ activity,
+ compiler)
+ return compiler_trace_file
+
+ # Should not arrive here.
+ raise ValueError('Unknown compiler type')
diff --git a/startop/scripts/iorap/compiler_device.py b/startop/scripts/iorap/compiler_device.py
new file mode 100644
index 0000000..d941cd9
--- /dev/null
+++ b/startop/scripts/iorap/compiler_device.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+import argparse
+import os
+import sys
+from typing import List
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR)) # framework/base/startop/script
+import lib.print_utils as print_utils
+import iorap.lib.iorapd_utils as iorapd_utils
+from app_startup.lib.app_runner import AppRunner
+
+IORAP_COMMON_BASH_SCRIPT = os.path.join(DIR, 'common')
+
+def parse_options(argv: List[str] = None):
+ """Parses command line arguments and returns an argparse Namespace object."""
+ parser = argparse.ArgumentParser(description="Compile perfetto trace file")
+ required_named = parser.add_argument_group('required named arguments')
+
+ required_named.add_argument('-i', dest='inodes', metavar='FILE',
+ help='Read cached inode data from a file saved '
+ 'earlier with pagecache.py -d')
+ required_named.add_argument('-p', dest='package',
+ help='Package of the app to be compiled')
+
+ optional_named = parser.add_argument_group('optional named arguments')
+ optional_named.add_argument('-o', dest='output',
+ help='The compiled trace is stored into the output file')
+ optional_named.add_argument('-a', dest='activity',
+ help='Activity of the app to be compiled')
+ optional_named.add_argument('-d', dest='debug', action='store_true'
+ , help='Activity of the app to be compiled')
+
+ return parser.parse_args(argv)
+
+def main(inodes, package, activity, output, **kwargs) -> int:
+ """Entries of the program."""
+ if not activity:
+ activity = AppRunner.get_activity(package)
+
+ passed = iorapd_utils.compile_perfetto_trace_on_device(package, activity,
+ inodes)
+ if passed and output:
+ iorapd_utils.get_iorapd_compiler_trace(package, activity, output)
+
+ return 0
+
+if __name__ == '__main__':
+ opts = parse_options()
+ if opts.debug:
+ print_utils.DEBUG = opts.debug
+ print_utils.debug_print(opts)
+ sys.exit(main(**(vars(opts))))
diff --git a/startop/scripts/iorap/compiler_ri.py b/startop/scripts/iorap/compiler_ri.py
new file mode 100755
index 0000000..90fc8a8
--- /dev/null
+++ b/startop/scripts/iorap/compiler_ri.py
@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+
+#
+# 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.
+#
+
+#
+# Dependencies:
+#
+# $> sudo apt-get install python3-pip
+# $> pip3 install --user protobuf sqlalchemy sqlite3
+#
+
+import optparse
+import os
+import re
+import sys
+import tempfile
+from pathlib import Path
+from datetime import timedelta
+from typing import Iterable, Optional, List
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.dirname(DIR))
+from iorap.generated.TraceFile_pb2 import *
+from iorap.lib.inode2filename import Inode2Filename
+
+parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(parent_dir_name)
+from trace_analyzer.lib.trace2db import Trace2Db, MmFilemapAddToPageCache, \
+ RawFtraceEntry
+import lib.cmd_utils as cmd_utils
+
+_PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes.
+ANDROID_BUILD_TOP = Path(parent_dir_name).parents[3]
+TRACECONV_BIN = ANDROID_BUILD_TOP.joinpath(
+ 'external/perfetto/tools/traceconv')
+
+class PageRun:
+ """
+ Intermediate representation for a run of one or more pages.
+ """
+ def __init__(self, device_number: int, inode: int, offset: int, length: int):
+ self.device_number = device_number
+ self.inode = inode
+ self.offset = offset
+ self.length = length
+
+ def __str__(self):
+ return "PageRun(device_number=%d, inode=%d, offset=%d, length=%d)" \
+ %(self.device_number, self.inode, self.offset, self.length)
+
+def debug_print(msg):
+ #print(msg)
+ pass
+
+UNDER_LAUNCH = False
+
+def page_cache_entries_to_runs(page_cache_entries: Iterable[MmFilemapAddToPageCache]):
+ global _PAGE_SIZE
+
+ runs = [
+ PageRun(device_number=pg_entry.dev, inode=pg_entry.ino, offset=pg_entry.ofs,
+ length=_PAGE_SIZE)
+ for pg_entry in page_cache_entries
+ ]
+
+ for r in runs:
+ debug_print(r)
+
+ print("Stats: Page runs totaling byte length: %d" %(len(runs) * _PAGE_SIZE))
+
+ return runs
+
+def optimize_page_runs(page_runs):
+ new_entries = []
+ last_entry = None
+ for pg_entry in page_runs:
+ if last_entry:
+ if pg_entry.device_number == last_entry.device_number and pg_entry.inode == last_entry.inode:
+ # we are dealing with a run for the same exact file as a previous run.
+ if pg_entry.offset == last_entry.offset + last_entry.length:
+ # trivially contiguous entries. merge them together.
+ last_entry.length += pg_entry.length
+ continue
+ # Default: Add the run without merging it to a previous run.
+ last_entry = pg_entry
+ new_entries.append(pg_entry)
+ return new_entries
+
+def is_filename_matching_filter(file_name, filters=[]):
+ """
+ Blacklist-style regular expression filters.
+
+ :return: True iff file_name has an RE match in one of the filters.
+ """
+ for filt in filters:
+ res = re.search(filt, file_name)
+ if res:
+ return True
+
+ return False
+
+def build_protobuf(page_runs, inode2filename, filters=[]):
+ trace_file = TraceFile()
+ trace_file_index = trace_file.index
+
+ file_id_counter = 0
+ file_id_map = {} # filename -> id
+
+ stats_length_total = 0
+ filename_stats = {} # filename -> total size
+
+ skipped_inode_map = {}
+ filtered_entry_map = {} # filename -> count
+
+ for pg_entry in page_runs:
+ fn = inode2filename.resolve(pg_entry.device_number, pg_entry.inode)
+ if not fn:
+ skipped_inode_map[pg_entry.inode] = skipped_inode_map.get(pg_entry.inode, 0) + 1
+ continue
+
+ filename = fn
+
+ if filters and not is_filename_matching_filter(filename, filters):
+ filtered_entry_map[filename] = filtered_entry_map.get(filename, 0) + 1
+ continue
+
+ file_id = file_id_map.get(filename)
+ # file_id could 0, which satisfies "if file_id" and causes duplicate
+ # filename for file id 0.
+ if file_id is None:
+ file_id = file_id_counter
+ file_id_map[filename] = file_id_counter
+ file_id_counter = file_id_counter + 1
+
+ file_index_entry = trace_file_index.entries.add()
+ file_index_entry.id = file_id
+ file_index_entry.file_name = filename
+
+ # already in the file index, add the file entry.
+ file_entry = trace_file.list.entries.add()
+ file_entry.index_id = file_id
+ file_entry.file_length = pg_entry.length
+ stats_length_total += file_entry.file_length
+ file_entry.file_offset = pg_entry.offset
+
+ filename_stats[filename] = filename_stats.get(filename, 0) + file_entry.file_length
+
+ for inode, count in skipped_inode_map.items():
+ print("WARNING: Skip inode %s because it's not in inode map (%d entries)" %(inode, count))
+
+ print("Stats: Sum of lengths %d" %(stats_length_total))
+
+ if filters:
+ print("Filter: %d total files removed." %(len(filtered_entry_map)))
+
+ for fn, count in filtered_entry_map.items():
+ print("Filter: File '%s' removed '%d' entries." %(fn, count))
+
+ for filename, file_size in filename_stats.items():
+ print("%s,%s" %(filename, file_size))
+
+ return trace_file
+
+def calc_trace_end_time(trace2db: Trace2Db,
+ trace_duration: Optional[timedelta]) -> float:
+ """
+ Calculates the end time based on the trace duration.
+ The start time is the first receiving mm file map event.
+ The end time is the start time plus the trace duration.
+ All of them are in milliseconds.
+ """
+ # If the duration is not set, assume all time is acceptable.
+ if trace_duration is None:
+ # float('inf')
+ return RawFtraceEntry.__table__.c.timestamp.type.python_type('inf')
+
+ first_event = trace2db.session.query(MmFilemapAddToPageCache).join(
+ MmFilemapAddToPageCache.raw_ftrace_entry).order_by(
+ RawFtraceEntry.timestamp).first()
+
+ # total_seconds() will return a float number.
+ return first_event.raw_ftrace_entry.timestamp + trace_duration.total_seconds()
+
+def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[timedelta]):
+ end_time = calc_trace_end_time(trace2db, trace_duration)
+ # SELECT * FROM tbl ORDER BY id;
+ return trace2db.session.query(MmFilemapAddToPageCache).join(
+ MmFilemapAddToPageCache.raw_ftrace_entry).filter(
+ RawFtraceEntry.timestamp <= end_time).order_by(
+ MmFilemapAddToPageCache.id).all()
+
+def transform_perfetto_trace_to_systrace(path_to_perfetto_trace: str,
+ path_to_tmp_systrace: str) -> None:
+ """ Transforms the systrace file from perfetto trace. """
+ cmd_utils.run_command_nofail([str(TRACECONV_BIN),
+ 'systrace',
+ path_to_perfetto_trace,
+ path_to_tmp_systrace])
+
+
+def run(sql_db_path:str,
+ trace_file:str,
+ trace_duration:Optional[timedelta],
+ output_file:str,
+ inode_table:str,
+ filter:List[str]) -> int:
+ trace2db = Trace2Db(sql_db_path)
+ # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache.
+ trace2db.set_raw_ftrace_entry_filter(\
+ lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache')
+ # TODO: parse multiple trace files here.
+ parse_count = trace2db.parse_file_into_db(trace_file)
+
+ mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db,
+ trace_duration)
+ print("DONE. Parsed %d entries into sql db." %(len(mm_filemap_add_to_page_cache_rows)))
+
+ page_runs = page_cache_entries_to_runs(mm_filemap_add_to_page_cache_rows)
+ print("DONE. Converted %d entries" %(len(page_runs)))
+
+ # TODO: flags to select optimizations.
+ optimized_page_runs = optimize_page_runs(page_runs)
+ print("DONE. Optimized down to %d entries" %(len(optimized_page_runs)))
+
+ print("Build protobuf...")
+ trace_file = build_protobuf(optimized_page_runs, inode_table, filter)
+
+ print("Write protobuf to file...")
+ output_file = open(output_file, 'wb')
+ output_file.write(trace_file.SerializeToString())
+ output_file.close()
+
+ print("DONE")
+
+ # TODO: Silent running mode [no output except on error] for build runs.
+
+ return 0
+
+def main(argv):
+ parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb")
+ parser.add_option('-i', dest='inode_data_file', metavar='FILE',
+ help='Read cached inode data from a file saved earlier with pagecache.py -d')
+ parser.add_option('-t', dest='trace_file', metavar='FILE',
+ help='Path to systrace file (trace.html) that will be parsed')
+ parser.add_option('--perfetto-trace', dest='perfetto_trace_file',
+ metavar='FILE',
+ help='Path to perfetto trace that will be parsed')
+
+ parser.add_option('--db', dest='sql_db', metavar='FILE',
+ help='Path to intermediate sqlite3 database [default: in-memory].')
+
+ parser.add_option('-f', dest='filter', action="append", default=[],
+ help="Add file filter. All file entries not matching one of the filters are discarded.")
+
+ parser.add_option('-l', dest='launch_lock', action="store_true", default=False,
+ help="Exclude all events not inside launch_lock")
+
+ parser.add_option('-o', dest='output_file', metavar='FILE',
+ help='Output protobuf file')
+
+ parser.add_option('--duration', dest='trace_duration', action="store",
+ type=int, help='The duration of trace in milliseconds.')
+
+ options, categories = parser.parse_args(argv[1:])
+
+ # TODO: OptionParser should have some flags to make these mandatory.
+ if not options.inode_data_file:
+ parser.error("-i is required")
+ if not options.trace_file and not options.perfetto_trace_file:
+ parser.error("one of -t or --perfetto-trace is required")
+ if options.trace_file and options.perfetto_trace_file:
+ parser.error("please enter either -t or --perfetto-trace, not both")
+ if not options.output_file:
+ parser.error("-o is required")
+
+ if options.launch_lock:
+ print("INFO: Launch lock flag (-l) enabled; filtering all events not inside launch_lock.")
+
+ inode_table = Inode2Filename.new_from_filename(options.inode_data_file)
+
+ sql_db_path = ":memory:"
+ if options.sql_db:
+ sql_db_path = options.sql_db
+
+ trace_duration = timedelta(milliseconds=options.trace_duration) if \
+ options.trace_duration is not None else None
+
+ # if the input is systrace
+ if options.trace_file:
+ return run(sql_db_path,
+ options.trace_file,
+ trace_duration,
+ options.output_file,
+ inode_table,
+ options.filter)
+
+ # if the input is perfetto trace
+ # TODO python 3.7 switch to using nullcontext
+ with tempfile.NamedTemporaryFile() as trace_file:
+ transform_perfetto_trace_to_systrace(options.perfetto_trace_file,
+ trace_file.name)
+ return run(sql_db_path,
+ trace_file.name,
+ trace_duration,
+ options.output_file,
+ inode_table,
+ options.filter)
+
+if __name__ == '__main__':
+ print(sys.argv)
+ sys.exit(main(sys.argv))
diff --git a/startop/scripts/iorap/compiler_test.py b/startop/scripts/iorap/compiler_test.py
new file mode 100644
index 0000000..b8de701
--- /dev/null
+++ b/startop/scripts/iorap/compiler_test.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+
+"""
+Unit tests for the compiler.py script.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> pytest compiler_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+import os
+
+import compiler_ri as compiler
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+TEXTCACHE = os.path.join(DIR, 'test_fixtures/compiler/common_textcache')
+SYSTRACE = os.path.join(DIR, 'test_fixtures/compiler/common_systrace')
+ARGV = [os.path.join(DIR, 'compiler.py'), '-i', TEXTCACHE, '-t', SYSTRACE]
+PERFETTO_TRACE = os.path.join(DIR,
+ 'test_fixtures/compiler/common_perfetto_trace.pb')
+
+def assert_compile_result(output, expected, *extra_argv):
+ argv = ARGV + ['-o', output] + [args for args in extra_argv]
+
+ compiler.main(argv)
+
+ with open(output, 'rb') as f1, open(expected, 'rb') as f2:
+ assert f1.read() == f2.read()
+
+### Unit tests - testing compiler code directly
+def test_transform_perfetto_trace_to_systrace(tmpdir):
+ expected = os.path.join(DIR,
+ 'test_fixtures/compiler/test_result_systrace')
+ output = tmpdir.mkdir('compiler').join('tmp_systrace')
+
+ compiler.transform_perfetto_trace_to_systrace(PERFETTO_TRACE, str(output))
+
+ with open(output, 'rb') as f1, open(expected, 'rb') as f2:
+ assert f1.read() == f2.read()
+
+### Functional tests - calls 'compiler.py --args...'
+def test_compiler_main(tmpdir):
+ output = tmpdir.mkdir('compiler').join('output')
+
+ # No duration
+ expected = os.path.join(DIR,
+ 'test_fixtures/compiler/test_result_without_duration.TraceFile.pb')
+ assert_compile_result(output, expected)
+
+ # 10ms duration
+ expected = os.path.join(DIR,
+ 'test_fixtures/compiler/test_result_with_duration.TraceFile.pb')
+ assert_compile_result(output, expected, '--duration', '10000')
+
+ # 30ms duration
+ expected = os.path.join(DIR,
+ 'test_fixtures/compiler/test_result_without_duration.TraceFile.pb')
+ assert_compile_result(output, expected, '--duration', '30000')
diff --git a/startop/scripts/iorap/dump_compiled_pb b/startop/scripts/iorap/dump_compiled_pb
new file mode 100755
index 0000000..ad26a7d
--- /dev/null
+++ b/startop/scripts/iorap/dump_compiled_pb
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+#
+# Dumps an iorap compiler protobuf from iorap.cmd.compiler into text
+# with gqui.
+#
+
+if [[ "$#" -lt 1 ]]; then
+ echo "Usage: $0 <compiler_trace_file.pb> [...args]" >&2
+ exit 1
+fi
+
+path_to_proto="$DIR/../../../../../system/iorap/src/serialize/TraceFile.proto"
+
+filename="$1"
+shift
+if ! [[ -f $filename ]]; then
+ echo "Error: $filename does not exist." >&2
+ exit 1
+fi
+
+gqui "rawproto:$filename" proto "$path_to_proto":iorap.serialize.proto.TraceFile "$@"
diff --git a/startop/scripts/iorap/dump_trace_pb b/startop/scripts/iorap/dump_trace_pb
new file mode 100755
index 0000000..bcec4a5
--- /dev/null
+++ b/startop/scripts/iorap/dump_trace_pb
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+#
+# Dumps a perfetto protobuf collected by iorapd (from perfetto) into text
+# with gqui.
+#
+
+if [[ "$#" -lt 1 ]]; then
+ echo "Usage: $0 <perfetto_trace.pb> [...args]" >&2
+ exit 1
+fi
+
+path_to_perfetto_proto="$DIR/../../../../../external/perfetto/protos/perfetto/trace/perfetto_trace.proto"
+
+filename="$1"
+shift
+if ! [[ -f $filename ]]; then
+ echo "Error: $filename does not exist." >&2
+ exit 1
+fi
+
+gqui "rawproto:$filename" proto "$path_to_perfetto_proto":perfetto.protos.Trace "$@"
diff --git a/startop/scripts/iorap/generated/TraceFile_pb2.py b/startop/scripts/iorap/generated/TraceFile_pb2.py
new file mode 100644
index 0000000..f005bed
--- /dev/null
+++ b/startop/scripts/iorap/generated/TraceFile_pb2.py
@@ -0,0 +1,259 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: TraceFile.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='TraceFile.proto',
+ package='iorap.serialize.proto',
+ syntax='proto2',
+ serialized_pb=_b('\n\x0fTraceFile.proto\x12\x15iorap.serialize.proto\"u\n\tTraceFile\x12\x34\n\x05index\x18\x01 \x02(\x0b\x32%.iorap.serialize.proto.TraceFileIndex\x12\x32\n\x04list\x18\x02 \x02(\x0b\x32$.iorap.serialize.proto.TraceFileList\"M\n\x0eTraceFileIndex\x12;\n\x07\x65ntries\x18\x01 \x03(\x0b\x32*.iorap.serialize.proto.TraceFileIndexEntry\"4\n\x13TraceFileIndexEntry\x12\n\n\x02id\x18\x01 \x02(\x03\x12\x11\n\tfile_name\x18\x02 \x02(\t\"G\n\rTraceFileList\x12\x36\n\x07\x65ntries\x18\x01 \x03(\x0b\x32%.iorap.serialize.proto.TraceFileEntry\"L\n\x0eTraceFileEntry\x12\x10\n\x08index_id\x18\x01 \x02(\x03\x12\x13\n\x0b\x66ile_offset\x18\x02 \x02(\x03\x12\x13\n\x0b\x66ile_length\x18\x03 \x02(\x03\x42\x1c\n\x18\x63om.google.android.iorapH\x03')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+
+_TRACEFILE = _descriptor.Descriptor(
+ name='TraceFile',
+ full_name='iorap.serialize.proto.TraceFile',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='index', full_name='iorap.serialize.proto.TraceFile.index', index=0,
+ number=1, type=11, cpp_type=10, label=2,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='list', full_name='iorap.serialize.proto.TraceFile.list', index=1,
+ number=2, type=11, cpp_type=10, label=2,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=42,
+ serialized_end=159,
+)
+
+
+_TRACEFILEINDEX = _descriptor.Descriptor(
+ name='TraceFileIndex',
+ full_name='iorap.serialize.proto.TraceFileIndex',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='entries', full_name='iorap.serialize.proto.TraceFileIndex.entries', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=161,
+ serialized_end=238,
+)
+
+
+_TRACEFILEINDEXENTRY = _descriptor.Descriptor(
+ name='TraceFileIndexEntry',
+ full_name='iorap.serialize.proto.TraceFileIndexEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='id', full_name='iorap.serialize.proto.TraceFileIndexEntry.id', index=0,
+ number=1, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_name', full_name='iorap.serialize.proto.TraceFileIndexEntry.file_name', index=1,
+ number=2, type=9, cpp_type=9, label=2,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=240,
+ serialized_end=292,
+)
+
+
+_TRACEFILELIST = _descriptor.Descriptor(
+ name='TraceFileList',
+ full_name='iorap.serialize.proto.TraceFileList',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='entries', full_name='iorap.serialize.proto.TraceFileList.entries', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=294,
+ serialized_end=365,
+)
+
+
+_TRACEFILEENTRY = _descriptor.Descriptor(
+ name='TraceFileEntry',
+ full_name='iorap.serialize.proto.TraceFileEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='index_id', full_name='iorap.serialize.proto.TraceFileEntry.index_id', index=0,
+ number=1, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_offset', full_name='iorap.serialize.proto.TraceFileEntry.file_offset', index=1,
+ number=2, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='file_length', full_name='iorap.serialize.proto.TraceFileEntry.file_length', index=2,
+ number=3, type=3, cpp_type=2, label=2,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=367,
+ serialized_end=443,
+)
+
+_TRACEFILE.fields_by_name['index'].message_type = _TRACEFILEINDEX
+_TRACEFILE.fields_by_name['list'].message_type = _TRACEFILELIST
+_TRACEFILEINDEX.fields_by_name['entries'].message_type = _TRACEFILEINDEXENTRY
+_TRACEFILELIST.fields_by_name['entries'].message_type = _TRACEFILEENTRY
+DESCRIPTOR.message_types_by_name['TraceFile'] = _TRACEFILE
+DESCRIPTOR.message_types_by_name['TraceFileIndex'] = _TRACEFILEINDEX
+DESCRIPTOR.message_types_by_name['TraceFileIndexEntry'] = _TRACEFILEINDEXENTRY
+DESCRIPTOR.message_types_by_name['TraceFileList'] = _TRACEFILELIST
+DESCRIPTOR.message_types_by_name['TraceFileEntry'] = _TRACEFILEENTRY
+
+TraceFile = _reflection.GeneratedProtocolMessageType('TraceFile', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILE,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFile)
+ ))
+_sym_db.RegisterMessage(TraceFile)
+
+TraceFileIndex = _reflection.GeneratedProtocolMessageType('TraceFileIndex', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEINDEX,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileIndex)
+ ))
+_sym_db.RegisterMessage(TraceFileIndex)
+
+TraceFileIndexEntry = _reflection.GeneratedProtocolMessageType('TraceFileIndexEntry', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEINDEXENTRY,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileIndexEntry)
+ ))
+_sym_db.RegisterMessage(TraceFileIndexEntry)
+
+TraceFileList = _reflection.GeneratedProtocolMessageType('TraceFileList', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILELIST,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileList)
+ ))
+_sym_db.RegisterMessage(TraceFileList)
+
+TraceFileEntry = _reflection.GeneratedProtocolMessageType('TraceFileEntry', (_message.Message,), dict(
+ DESCRIPTOR = _TRACEFILEENTRY,
+ __module__ = 'TraceFile_pb2'
+ # @@protoc_insertion_point(class_scope:iorap.serialize.proto.TraceFileEntry)
+ ))
+_sym_db.RegisterMessage(TraceFileEntry)
+
+
+DESCRIPTOR.has_options = True
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\030com.google.android.iorapH\003'))
+# @@protoc_insertion_point(module_scope)
diff --git a/startop/scripts/iorap/generated/codegen_protos b/startop/scripts/iorap/generated/codegen_protos
new file mode 100755
index 0000000..5688711
--- /dev/null
+++ b/startop/scripts/iorap/generated/codegen_protos
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+APROTOC="$(which aprotoc)"
+
+IORAP_SERIALIZE_DIR="${DIR}/../../../../../../system/iorap/src/serialize"
+IORAP_PROTOS=($IORAP_SERIALIZE_DIR/*.proto)
+
+if [[ $? -ne 0 ]]; then
+ echo "Fatal: Missing aprotoc. Set APROTOC=... or lunch build/envsetup.sh?" >&2
+ exit 1
+fi
+
+if ! [[ -d $IORAP_SERIALIZE_DIR ]]; then
+ echo "Fatal: Directory '$IORAP_SERIALIZE_DIR' does not exist." >&2
+ exit 1
+fi
+
+# codegen the .py files into the same directory as this script.
+echo "$APROTOC" --proto_path="$IORAP_SERIALIZE_DIR" --python_out="$DIR" "${IORAP_PROTOS[@]}"
+"$APROTOC" --proto_path="$IORAP_SERIALIZE_DIR" --python_out="$DIR" "${IORAP_PROTOS[@]}"
diff --git a/startop/scripts/iorap/lib/inode2filename.py b/startop/scripts/iorap/lib/inode2filename.py
new file mode 100644
index 0000000..2e71393
--- /dev/null
+++ b/startop/scripts/iorap/lib/inode2filename.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+
+#
+# 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.
+#
+
+from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union, TextIO
+
+import re
+
+class Inode2Filename:
+ """
+ Parses a text file of the format
+ "uint(dev_t) uint(ino_t) int(file_size) string(filepath)\\n"*
+
+ Lines not matching this format are ignored.
+ """
+
+ def __init__(self, inode_data_file: TextIO):
+ """
+ Create an Inode2Filename that reads cached inode from a file saved earlier
+ (e.g. with pagecache.py -d or with inode2filename --format=textcache)
+
+ :param inode_data_file: a file object (e.g. created with open or StringIO).
+
+ Lifetime: inode_data_file is only used during the construction of the object.
+ """
+ self._inode_table = Inode2Filename.build_inode_lookup_table(inode_data_file)
+
+ @classmethod
+ def new_from_filename(cls, textcache_filename: str) -> 'Inode2Filename':
+ """
+ Create an Inode2Filename that reads cached inode from a file saved earlier
+ (e.g. with pagecache.py -d or with inode2filename --format=textcache)
+
+ :param textcache_filename: path to textcache
+ """
+ with open(textcache_filename) as inode_data_file:
+ return cls(inode_data_file)
+
+ @staticmethod
+ def build_inode_lookup_table(inode_data_file: TextIO) -> Dict[Tuple[int, int], Tuple[str, str]]:
+ """
+ :return: map { (device_int, inode_int) -> (filename_str, size_str) }
+ """
+ inode2filename = {}
+ for line in inode_data_file:
+ # stat -c "%d %i %s %n
+ # device number, inode number, total size in bytes, file name
+ result = re.match('([0-9]+)d? ([0-9]+) -?([0-9]+) (.*)', line)
+ if result:
+ inode2filename[(int(result.group(1)), int(result.group(2)))] = \
+ (result.group(4), result.group(3))
+
+ return inode2filename
+
+ def resolve(self, dev_t: int, ino_t: int) -> Optional[str]:
+ """
+ Return a filename (str) from a (dev_t, ino_t) inode pair.
+
+ Returns None if the lookup fails.
+ """
+ maybe_result = self._inode_table.get((dev_t, ino_t))
+
+ if not maybe_result:
+ return None
+
+ return maybe_result[0] # filename str
+
+ def __len__(self) -> int:
+ """
+ :return: the number of inode entries parsed from the file.
+ """
+ return len(self._inode_table)
+
+ def __repr__(self) -> str:
+ """
+ :return: string representation for debugging/test failures.
+ """
+ return "Inode2Filename%s" %(repr(self._inode_table))
+
+ # end of class.
diff --git a/startop/scripts/iorap/lib/inode2filename_test.py b/startop/scripts/iorap/lib/inode2filename_test.py
new file mode 100755
index 0000000..1224c61
--- /dev/null
+++ b/startop/scripts/iorap/lib/inode2filename_test.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+
+"""
+Unit tests for inode2filename module.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./inode2filename_test.py
+ $> pytest inode2filename_test.py
+ $> python -m pytest inode2filename_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+from contextlib import contextmanager
+import io
+import shlex
+import sys
+import typing
+
+# pip imports
+import pytest
+
+# local imports
+from inode2filename import *
+
+def create_inode2filename(*contents):
+ buf = io.StringIO()
+
+ for c in contents:
+ buf.write(c)
+ buf.write("\n")
+
+ buf.seek(0)
+
+ i2f = Inode2Filename(buf)
+
+ buf.close()
+
+ return i2f
+
+def test_inode2filename():
+ a = create_inode2filename("")
+ assert len(a) == 0
+ assert a.resolve(1, 2) == None
+
+ a = create_inode2filename("1 2 3 foo.bar")
+ assert len(a) == 1
+ assert a.resolve(1, 2) == "foo.bar"
+ assert a.resolve(4, 5) == None
+
+ a = create_inode2filename("1 2 3 foo.bar", "4 5 6 bar.baz")
+ assert len(a) == 2
+ assert a.resolve(1, 2) == "foo.bar"
+ assert a.resolve(4, 5) == "bar.baz"
+
+ a = create_inode2filename("1567d 8910 -1 /a/b/c/", "4 5 6 bar.baz")
+ assert len(a) == 2
+ assert a.resolve(1567, 8910) == "/a/b/c/"
+ assert a.resolve(4, 5) == "bar.baz"
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/iorap/lib/iorapd_utils.py b/startop/scripts/iorap/lib/iorapd_utils.py
new file mode 100644
index 0000000..f6f21fd
--- /dev/null
+++ b/startop/scripts/iorap/lib/iorapd_utils.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Helper util libraries for iorapd related operations."""
+
+import os
+import sys
+
+# up to two level
+sys.path.append(os.path.join(os.path.abspath(__file__),'../..'))
+import lib.cmd_utils as cmd_utils
+
+IORAPID_LIB_DIR = os.path.abspath(os.path.dirname(__file__))
+IORAPD_DATA_PATH = '/data/misc/iorapd'
+IORAP_COMMON_BASH_SCRIPT = os.path.realpath(os.path.join(IORAPID_LIB_DIR,
+ '../common'))
+
+def _iorapd_path_to_data_file(package: str, activity: str, suffix: str) -> str:
+ """Gets conventional data filename.
+
+ Returns:
+ The path of iorapd data file.
+
+ """
+ # Match logic of 'AppComponentName' in iorap::compiler C++ code.
+ return '{}/{}%2F{}.{}'.format(IORAPD_DATA_PATH, package, activity, suffix)
+
+def compile_perfetto_trace_on_device(package: str, activity: str,
+ inodes: str) -> bool:
+ """Compiles the perfetto trace using on-device compiler."""
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_compiler_for_app_trace',
+ [package, activity, inodes])
+ return passed
+
+def get_iorapd_compiler_trace(package: str, activity: str, dest: str) -> str:
+ """Gets compiler trace to dest file."""
+ src = _iorapd_path_to_data_file(package, activity, 'compiled_trace.pb')
+ passed, _ = cmd_utils.run_shell_command('adb pull "{}" "{}"'.format(src, dest))
+ if not passed:
+ return False
+ return True
+
+def iorapd_compiler_install_trace_file(package: str, activity: str,
+ input_file: str) -> bool:
+ """Installs a compiled trace file.
+
+ Returns:
+ Whether the trace file is installed successful or not.
+ """
+ # remote path calculations
+ compiled_path = _iorapd_path_to_data_file(package, activity,
+ 'compiled_trace.pb')
+
+ if not os.path.exists(input_file):
+ print('Error: File {} does not exist'.format(input_file))
+ return False
+
+ passed, _ = cmd_utils.run_adb_shell_command(
+ 'mkdir -p "$(dirname "{}")"'.format(compiled_path))
+ if not passed:
+ return False
+
+ passed, _ = cmd_utils.run_shell_command('adb push "{}" "{}"'.format(
+ input_file, compiled_path))
+
+ return passed
+
+def wait_for_iorapd_finish(package: str,
+ activity: str,
+ timeout: int,
+ debug: bool,
+ logcat_timestamp: str)->bool:
+ """Waits for the finish of iorapd.
+
+ Returns:
+ A bool indicates whether the iorapd is done successfully or not.
+ """
+ # Set verbose for bash script based on debug flag.
+ if debug:
+ os.putenv('verbose', 'y')
+
+ # Validate that readahead completes.
+ # If this fails for some reason, then this will also discard the timing of
+ # the run.
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_readahead_wait_until_finished',
+ [package, activity, logcat_timestamp,
+ str(timeout)])
+ return passed
+
+
+def enable_iorapd_readahead() -> bool:
+ """
+ Disable readahead. Subsequent launches of an application will be sped up
+ by iorapd readahead prefetching.
+
+ Returns:
+ A bool indicates whether the enabling is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_readahead_enable', [])
+ return passed
+
+def disable_iorapd_readahead() -> bool:
+ """
+ Disable readahead. Subsequent launches of an application will be not be sped
+ up by iorapd readahead prefetching.
+
+ Returns:
+ A bool indicates whether the disabling is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_readahead_disable', [])
+ return passed
+
+def enable_iorapd_perfetto() -> bool:
+ """
+ Enable Perfetto. Subsequent launches of an application will record a perfetto
+ trace protobuf.
+
+ Returns:
+ A bool indicates whether the enabling is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_perfetto_enable', [])
+ return passed
+
+def disable_iorapd_perfetto() -> bool:
+ """
+ Disable Perfetto. Subsequent launches of applications will no longer record
+ perfetto trace protobufs.
+
+ Returns:
+ A bool indicates whether the disabling is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_perfetto_disable', [])
+ return passed
+
+def start_iorapd() -> bool:
+ """
+ Starts iorapd.
+
+ Returns:
+ A bool indicates whether the starting is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_start', [])
+ return passed
+
+def stop_iorapd() -> bool:
+ """
+ Stops iorapd.
+
+ Returns:
+ A bool indicates whether the stopping is done successfully or not.
+ """
+ passed, _ = cmd_utils.run_shell_func(IORAP_COMMON_BASH_SCRIPT,
+ 'iorapd_stop', [])
+ return passed
+
diff --git a/startop/scripts/iorap/pull_textcache b/startop/scripts/iorap/pull_textcache
new file mode 100755
index 0000000..0554426
--- /dev/null
+++ b/startop/scripts/iorap/pull_textcache
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+if [[ $# -lt 1 ]]; then
+ echo "Usage: $0 <output-filename>" >&2
+ exit 1
+fi
+
+# see compiler/main.cc for list of roots
+adb shell iorap.inode2filename --output-format=textcache --output=/data/local/tmp/dumpcache --all --root=/system --root=/apex --root=/vendor --root=/data --root=/product --root=/metadata
+adb pull /data/local/tmp/dumpcache "$1"
diff --git a/startop/scripts/iorap/test_fixtures/compiler/common_perfetto_trace.pb b/startop/scripts/iorap/test_fixtures/compiler/common_perfetto_trace.pb
new file mode 100644
index 0000000..a47ad3d
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/common_perfetto_trace.pb
Binary files differ
diff --git a/startop/scripts/iorap/test_fixtures/compiler/common_systrace b/startop/scripts/iorap/test_fixtures/compiler/common_systrace
new file mode 100644
index 0000000..4573738
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/common_systrace
@@ -0,0 +1,5 @@
+<...>-2965 (-----) [001] .... 10000.746629: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000679ee1ec pfn=1299913 ofs=192512
+<...>-2965 (-----) [001] .... 10010.746664: mm_filemap_add_to_page_cache: dev 253:6 ino 2 page=0000000006cd2fb7 pfn=1296251 ofs=196608
+<...>-2965 (-----) [001] .... 10020.746677: mm_filemap_add_to_page_cache: dev 253:6 ino 3 page=00000000af82f3d6 pfn=1419330 ofs=200704
+<...>-2965 (-----) [001] .... 10030.746693: mm_filemap_add_to_page_cache: dev 253:6 ino 4 page=000000002840f054 pfn=1304928 ofs=204800
+<...>-2965 (-----) [001] .... 10040.746706: mm_filemap_add_to_page_cache: dev 253:6 ino 5 page=000000004a59da17 pfn=1288069 ofs=208896
diff --git a/startop/scripts/iorap/test_fixtures/compiler/common_textcache b/startop/scripts/iorap/test_fixtures/compiler/common_textcache
new file mode 100644
index 0000000..da03004
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/common_textcache
@@ -0,0 +1,2 @@
+64774 1 -1 /system/test1
+64774 3 -1 /data/test2
diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace
new file mode 100644
index 0000000..59ff753
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace
@@ -0,0 +1,748 @@
+TRACE:
+# tracer: nop
+#
+# entries-in-buffer/entries-written: 30624/30624 #P:4
+#
+# _-----=> irqs-off
+# / _----=> need-resched
+# | / _---=> hardirq/softirq
+# || / _--=> preempt-depth
+# ||| / delay
+# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION
+# | | | | |||| | |
+ <unknown>-27388 (-----) [004] .... 1920260.530929: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1461937 ofs=9535488
+ <unknown>-27388 (-----) [005] .... 1920260.532161: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1344589 ofs=9474048
+ <unknown>-27388 (-----) [005] .... 1920260.532183: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1153671 ofs=9478144
+ <unknown>-27388 (-----) [005] .... 1920260.532184: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1219563 ofs=9482240
+ <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1083162 ofs=9486336
+ <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1147318 ofs=9490432
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1333594 ofs=9494528
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1375715 ofs=9498624
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1184831 ofs=9502720
+ <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1241653 ofs=9506816
+ <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1134975 ofs=9510912
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1145772 ofs=9515008
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1090457 ofs=9519104
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1137942 ofs=9523200
+ <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1130123 ofs=9527296
+ <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1208783 ofs=9531392
+ <unknown>-27388 (-----) [005] .... 1920260.532192: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1294989 ofs=9539584
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1163979 ofs=9543680
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1350628 ofs=9547776
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1386717 ofs=9551872
+ <unknown>-27388 (-----) [005] .... 1920260.532207: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1316148 ofs=9555968
+ <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1316419 ofs=9560064
+ <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1149076 ofs=9564160
+ <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1372772 ofs=9568256
+ <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1116389 ofs=9572352
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1325458 ofs=9576448
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1195423 ofs=9580544
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1250964 ofs=9584640
+ <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1196027 ofs=9588736
+ <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1354059 ofs=9592832
+ <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1264649 ofs=9596928
+ <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1245285 ofs=9601024
+ <unknown>-27388 (-----) [005] .... 1920260.535119: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1411552 ofs=44244992
+ <unknown>-27388 (-----) [005] .... 1920260.535129: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1483081 ofs=433524736
+ <unknown>-27388 (-----) [004] .... 1920260.536144: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1276173 ofs=438185984
+ <unknown>-27388 (-----) [004] .... 1920260.536462: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1174575 ofs=44249088
+ <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1126294 ofs=44253184
+ <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1248232 ofs=44257280
+ <unknown>-27388 (-----) [004] .... 1920260.537065: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1332993 ofs=44240896
+ <unknown>-27388 (-----) [006] .... 1920260.537646: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1153343 ofs=44400640
+ <unknown>-27388 (-----) [005] .... 1920260.538777: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1358397 ofs=44474368
+ <unknown>-12683 (-----) [006] .... 1920260.560094: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1426577 ofs=0
+ <unknown>-12683 (-----) [006] .... 1920260.560105: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117587 ofs=1171456
+ <unknown>-12683 (-----) [006] .... 1920260.561199: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099987 ofs=4096
+ <unknown>-12683 (-----) [006] .... 1920260.561411: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099910 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.561598: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099905 ofs=20480
+ <unknown>-12683 (-----) [006] .... 1920260.561758: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099883 ofs=32768
+ <unknown>-12683 (-----) [006] .... 1920260.562088: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099809 ofs=36864
+ <unknown>-12683 (-----) [006] .... 1920260.562325: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099803 ofs=98304
+ <unknown>-12683 (-----) [006] .... 1920260.562516: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099795 ofs=102400
+ <unknown>-12683 (-----) [006] .... 1920260.563094: mm_filemap_add_to_page_cache: dev 0:64768 ino 5f3 page=0000000000000000 pfn=1107649 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.563105: mm_filemap_add_to_page_cache: dev 0:64768 ino 5f3 page=0000000000000000 pfn=1269029 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.563785: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1451096 ofs=8192
+ <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1301480 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1314353 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.563791: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1216744 ofs=24576
+ <unknown>-12683 (-----) [006] .... 1920260.564309: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099787 ofs=49152
+ <unknown>-12683 (-----) [006] .... 1920260.564514: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099778 ofs=53248
+ <unknown>-12683 (-----) [005] .... 1920260.564756: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1148849 ofs=114688
+ <unknown>-12683 (-----) [005] .... 1920260.564973: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1164731 ofs=118784
+ <unknown>-12683 (-----) [005] .... 1920260.565000: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1170255 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.565003: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1181043 ofs=4096
+ <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1296004 ofs=8192
+ <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1102004 ofs=12288
+ <unknown>-12683 (-----) [005] .... 1920260.565626: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1351232 ofs=470597632
+ <unknown>-12683 (-----) [005] .... 1920260.565982: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1391336 ofs=40210432
+ <unknown>-12683 (-----) [005] .... 1920260.565985: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267536 ofs=12668928
+ <unknown>-27388 (-----) [007] .... 1920260.566082: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1256752 ofs=43921408
+ <unknown>-12683 (-----) [005] .... 1920260.566516: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1110966 ofs=176226304
+ <unknown>-12683 (-----) [005] .... 1920260.566519: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1060586 ofs=12967936
+ <unknown>-12683 (-----) [004] .... 1920260.567773: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117234 ofs=421888
+ <unknown>-12683 (-----) [005] .... 1920260.568604: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1210571 ofs=430080
+ <unknown>-12683 (-----) [005] .... 1920260.568887: mm_filemap_add_to_page_cache: dev 0:64771 ino 69 page=0000000000000000 pfn=1055640 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.568908: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1142694 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.568910: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1060788 ofs=299008
+ <unknown>-12683 (-----) [005] .... 1920260.569418: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1085046 ofs=4096
+ <unknown>-12683 (-----) [005] .... 1920260.569640: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1057135 ofs=8192
+ <unknown>-12683 (-----) [005] .... 1920260.569833: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1058976 ofs=19406848
+ <unknown>-12683 (-----) [005] .... 1920260.569835: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1477947 ofs=10526720
+ <unknown>-12683 (-----) [005] .... 1920260.572285: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1237492 ofs=299008
+ <unknown>-12683 (-----) [005] .... 1920260.572297: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1264914 ofs=339968
+ <unknown>-12683 (-----) [005] .... 1920260.572314: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1434748 ofs=348160
+ <unknown>-12683 (-----) [005] .... 1920260.572316: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1372959 ofs=352256
+ <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1258955 ofs=356352
+ <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1113420 ofs=360448
+ <unknown>-12683 (-----) [005] .... 1920260.572318: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1137083 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.575490: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1379679 ofs=65536
+ <unknown>-12683 (-----) [006] .... 1920260.576194: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1323898 ofs=69632
+ <unknown>-12683 (-----) [006] .... 1920260.576248: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323895 ofs=262623232
+ <unknown>-12683 (-----) [006] .... 1920260.576251: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323861 ofs=13156352
+ <unknown>-12683 (-----) [005] .... 1920260.576810: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1477585 ofs=262590464
+ <unknown>-12683 (-----) [004] .... 1920260.577197: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1267617 ofs=25206784
+ <unknown>-12683 (-----) [004] .... 1920260.577200: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267618 ofs=12636160
+ <unknown>-12683 (-----) [005] .... 1920260.577725: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1056225 ofs=228618240
+ <unknown>-12683 (-----) [005] .... 1920260.577727: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1164942 ofs=13082624
+ <unknown>-12683 (-----) [007] .... 1920260.578411: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1372616 ofs=0
+ <unknown>-12683 (-----) [007] .... 1920260.578422: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1307468 ofs=4096
+ <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1120117 ofs=8192
+ <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1217989 ofs=12288
+ <unknown>-12683 (-----) [007] .... 1920260.578650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1475011 ofs=5419008
+ <unknown>-12683 (-----) [007] .... 1920260.578653: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1066084 ofs=236453888
+ <unknown>-12683 (-----) [007] .... 1920260.578654: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1100271 ofs=13099008
+ <unknown>-12683 (-----) [004] .... 1920260.579004: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1485156 ofs=5423104
+ <unknown>-12683 (-----) [004] .... 1920260.579005: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1124212 ofs=5427200
+ <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1195377 ofs=5431296
+ <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1265888 ofs=5435392
+ <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1170194 ofs=5439488
+ <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1403742 ofs=5443584
+ <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1123826 ofs=5447680
+ <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1255034 ofs=5451776
+ <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1190447 ofs=5455872
+ <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1286864 ofs=5459968
+ <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1428535 ofs=5464064
+ <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1184092 ofs=5468160
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1411906 ofs=5472256
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1342349 ofs=5476352
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1188185 ofs=5480448
+ <unknown>-12683 (-----) [004] .... 1920260.579014: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1158702 ofs=5484544
+ <unknown>-12683 (-----) [005] .... 1920260.579430: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1299421 ofs=5230592
+ <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1317097 ofs=5234688
+ <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1441714 ofs=5238784
+ <unknown>-12683 (-----) [005] .... 1920260.579438: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1081974 ofs=5242880
+ <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1128684 ofs=5246976
+ <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447381 ofs=5251072
+ <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1466410 ofs=5255168
+ <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1259909 ofs=5259264
+ <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1125784 ofs=5263360
+ <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1270592 ofs=5267456
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246070 ofs=5271552
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1472544 ofs=5275648
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1113357 ofs=5279744
+ <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1202021 ofs=5283840
+ <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078639 ofs=5287936
+ <unknown>-12683 (-----) [005] .... 1920260.579449: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1176171 ofs=5292032
+ <unknown>-12683 (-----) [005] .... 1920260.579450: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1089516 ofs=5296128
+ <unknown>-12683 (-----) [005] .... 1920260.579451: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1400065 ofs=5300224
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1300489 ofs=5304320
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1452081 ofs=5308416
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1161862 ofs=5312512
+ <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1161871 ofs=5316608
+ <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1263798 ofs=5320704
+ <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1126887 ofs=5324800
+ <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1375498 ofs=5328896
+ <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1328067 ofs=5332992
+ <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1420691 ofs=5337088
+ <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1298707 ofs=5341184
+ <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078670 ofs=5345280
+ <unknown>-12683 (-----) [005] .... 1920260.579457: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1430498 ofs=5349376
+ <unknown>-12683 (-----) [005] .... 1920260.579458: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1338720 ofs=5353472
+ <unknown>-12683 (-----) [005] .... 1920260.579476: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1452611 ofs=5357568
+ <unknown>-12683 (-----) [006] .... 1920260.580451: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241967 ofs=0
+ <unknown>-12683 (-----) [006] .... 1920260.580454: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1116541 ofs=4096
+ <unknown>-12683 (-----) [006] .... 1920260.580461: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1145049 ofs=8192
+ <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1277255 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1098037 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.580463: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1135986 ofs=20480
+ <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1154455 ofs=24576
+ <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1221822 ofs=28672
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1078684 ofs=32768
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1158876 ofs=36864
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1289644 ofs=40960
+ <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1289386 ofs=45056
+ <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1131002 ofs=49152
+ <unknown>-12683 (-----) [006] .... 1920260.580467: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1464335 ofs=53248
+ <unknown>-12683 (-----) [006] .... 1920260.580468: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1135789 ofs=57344
+ <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1240897 ofs=61440
+ <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241770 ofs=65536
+ <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1421959 ofs=69632
+ <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1230007 ofs=73728
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1109271 ofs=77824
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1159974 ofs=81920
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1154528 ofs=86016
+ <unknown>-12683 (-----) [006] .... 1920260.580472: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1315790 ofs=90112
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1185583 ofs=94208
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1253153 ofs=98304
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103982 ofs=102400
+ <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1284589 ofs=106496
+ <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1169601 ofs=110592
+ <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1206248 ofs=114688
+ <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1261161 ofs=118784
+ <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1305841 ofs=122880
+ <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1468293 ofs=126976
+ <unknown>-12683 (-----) [004] .... 1920260.580646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1318816 ofs=16384
+ <unknown>-12683 (-----) [004] .... 1920260.580649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1472922 ofs=20480
+ <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1473229 ofs=24576
+ <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1524262 ofs=28672
+ <unknown>-12683 (-----) [004] .... 1920260.580656: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1205714 ofs=32768
+ <unknown>-12683 (-----) [004] .... 1920260.580657: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1310560 ofs=36864
+ <unknown>-12683 (-----) [004] .... 1920260.580658: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1295070 ofs=40960
+ <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1404093 ofs=45056
+ <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1435814 ofs=49152
+ <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1435442 ofs=53248
+ <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1096077 ofs=57344
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1483793 ofs=61440
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1231298 ofs=65536
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1215648 ofs=69632
+ <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1327326 ofs=73728
+ <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1108894 ofs=77824
+ <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1327545 ofs=81920
+ <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1328804 ofs=86016
+ <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1300171 ofs=90112
+ <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353250 ofs=94208
+ <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1333681 ofs=98304
+ <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1144969 ofs=102400
+ <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1450962 ofs=106496
+ <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1255701 ofs=110592
+ <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1294782 ofs=114688
+ <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1226912 ofs=118784
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1294579 ofs=122880
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246960 ofs=126976
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1199086 ofs=131072
+ <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1449590 ofs=135168
+ <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1276363 ofs=139264
+ <unknown>-12683 (-----) [004] .... 1920260.580675: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1389998 ofs=143360
+ <unknown>-12683 (-----) [004] .... 1920260.580739: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1423031 ofs=1249280
+ <unknown>-12683 (-----) [004] .... 1920260.580741: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1171032 ofs=1253376
+ <unknown>-12683 (-----) [004] .... 1920260.580742: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1320946 ofs=1257472
+ <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1314696 ofs=1261568
+ <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1414864 ofs=1265664
+ <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1334933 ofs=1269760
+ <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1242845 ofs=1273856
+ <unknown>-12683 (-----) [004] .... 1920260.580747: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1289488 ofs=1277952
+ <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1335445 ofs=1282048
+ <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1289663 ofs=1286144
+ <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1080462 ofs=1290240
+ <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1286303 ofs=1294336
+ <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353531 ofs=1298432
+ <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1280701 ofs=1302528
+ <unknown>-12683 (-----) [004] .... 1920260.580751: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1107730 ofs=1306624
+ <unknown>-12683 (-----) [004] .... 1920260.580752: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1242729 ofs=1310720
+ <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078336 ofs=1314816
+ <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1372425 ofs=1318912
+ <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1248813 ofs=1323008
+ <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1201155 ofs=1327104
+ <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1250103 ofs=1331200
+ <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1359710 ofs=1335296
+ <unknown>-12683 (-----) [004] .... 1920260.580756: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1272462 ofs=1339392
+ <unknown>-12683 (-----) [004] .... 1920260.580758: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1097035 ofs=1343488
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1233124 ofs=1347584
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1455812 ofs=1351680
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1355689 ofs=1355776
+ <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1263593 ofs=1359872
+ <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1230789 ofs=1363968
+ <unknown>-12683 (-----) [004] .... 1920260.580761: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1143766 ofs=1368064
+ <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1269666 ofs=1372160
+ <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353022 ofs=1376256
+ <unknown>-12683 (-----) [004] .... 1920260.581613: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1355509 ofs=258048
+ <unknown>-12683 (-----) [004] .... 1920260.581615: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1178902 ofs=262144
+ <unknown>-12683 (-----) [004] .... 1920260.581616: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1193649 ofs=266240
+ <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1225497 ofs=270336
+ <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1228259 ofs=274432
+ <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1309674 ofs=278528
+ <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1239390 ofs=282624
+ <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1468083 ofs=286720
+ <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1292751 ofs=290816
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1318066 ofs=294912
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1489314 ofs=299008
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1169867 ofs=303104
+ <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1314256 ofs=307200
+ <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1310230 ofs=311296
+ <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1356180 ofs=315392
+ <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1419179 ofs=319488
+ <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1307265 ofs=323584
+ <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1218590 ofs=327680
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447586 ofs=331776
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1209382 ofs=335872
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1072148 ofs=339968
+ <unknown>-12683 (-----) [004] .... 1920260.581645: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1227195 ofs=344064
+ <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246369 ofs=348160
+ <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1193845 ofs=352256
+ <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1137553 ofs=356352
+ <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1475215 ofs=360448
+ <unknown>-12683 (-----) [004] .... 1920260.581648: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1258935 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1448788 ofs=368640
+ <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447611 ofs=372736
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1290842 ofs=376832
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447826 ofs=380928
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1181016 ofs=385024
+ <unknown>-12683 (-----) [005] .... 1920260.582230: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1216810 ofs=1662976
+ <unknown>-12683 (-----) [005] .... 1920260.582234: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1175966 ofs=1667072
+ <unknown>-12683 (-----) [005] .... 1920260.582235: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1449798 ofs=1671168
+ <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1273480 ofs=1675264
+ <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1152779 ofs=1679360
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1272810 ofs=1683456
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1248634 ofs=1687552
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1203376 ofs=1691648
+ <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1138880 ofs=1695744
+ <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1344591 ofs=1699840
+ <unknown>-12683 (-----) [005] .... 1920260.582239: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1416060 ofs=1703936
+ <unknown>-12683 (-----) [005] .... 1920260.582246: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1128676 ofs=1708032
+ <unknown>-12683 (-----) [005] .... 1920260.582247: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1301921 ofs=1712128
+ <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1384569 ofs=1716224
+ <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1249106 ofs=1720320
+ <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1206596 ofs=1724416
+ <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1429831 ofs=1728512
+ <unknown>-12683 (-----) [005] .... 1920260.582252: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1107796 ofs=1732608
+ <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1098336 ofs=1736704
+ <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1230286 ofs=1740800
+ <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1100370 ofs=1744896
+ <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241930 ofs=1748992
+ <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1366807 ofs=1753088
+ <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1136252 ofs=1757184
+ <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1274291 ofs=1761280
+ <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1254775 ofs=1765376
+ <unknown>-12683 (-----) [005] .... 1920260.582259: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1194679 ofs=1769472
+ <unknown>-12683 (-----) [005] .... 1920260.582262: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1177090 ofs=1773568
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1343925 ofs=1777664
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1159217 ofs=1781760
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435471 ofs=1785856
+ <unknown>-12683 (-----) [005] .... 1920260.582264: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435529 ofs=1789952
+ <unknown>-12683 (-----) [004] .... 1920260.582524: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1181910 ofs=0
+ <unknown>-12683 (-----) [004] .... 1920260.582528: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1212021 ofs=4096
+ <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1162778 ofs=8192
+ <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1107700 ofs=12288
+ <unknown>-12683 (-----) [004] .... 1920260.583553: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1093394 ofs=3399680
+ <unknown>-12683 (-----) [004] .... 1920260.583984: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1121431 ofs=242503680
+ <unknown>-12683 (-----) [004] .... 1920260.583986: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1168551 ofs=13115392
+ <unknown>-12683 (-----) [004] .... 1920260.584304: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347409 ofs=0
+ <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1428681 ofs=4096
+ <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1259106 ofs=8192
+ <unknown>-12683 (-----) [004] .... 1920260.584308: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1343229 ofs=12288
+ <unknown>-12694 (-----) [005] .... 1920260.584622: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1098733 ofs=1531904
+ <unknown>-12696 (-----) [006] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331319 ofs=1536000
+ <unknown>-12694 (-----) [005] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278537 ofs=1540096
+ <unknown>-12696 (-----) [006] .... 1920260.584631: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1492534 ofs=1544192
+ <unknown>-12694 (-----) [005] .... 1920260.584636: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1460878 ofs=1548288
+ <unknown>-12694 (-----) [005] .... 1920260.584640: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092973 ofs=1552384
+ <unknown>-12694 (-----) [005] .... 1920260.584641: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1103200 ofs=1556480
+ <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1257426 ofs=1560576
+ <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1219424 ofs=1564672
+ <unknown>-12683 (-----) [004] .... 1920260.584660: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279352 ofs=1568768
+ <unknown>-12696 (-----) [006] .... 1920260.584662: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260572 ofs=1572864
+ <unknown>-12683 (-----) [004] .... 1920260.584663: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1225809 ofs=1576960
+ <unknown>-12696 (-----) [006] .... 1920260.584665: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1350766 ofs=1585152
+ <unknown>-12697 (-----) [007] .... 1920260.584666: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1107173 ofs=1581056
+ <unknown>-12683 (-----) [004] .... 1920260.584668: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1305885 ofs=1589248
+ <unknown>-12694 (-----) [005] .... 1920260.584669: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1293385 ofs=1593344
+ <unknown>-12696 (-----) [006] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1173841 ofs=1597440
+ <unknown>-12697 (-----) [007] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080021 ofs=1601536
+ <unknown>-12683 (-----) [004] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1147419 ofs=1605632
+ <unknown>-12696 (-----) [006] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1252762 ofs=1609728
+ <unknown>-12694 (-----) [005] .... 1920260.584674: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1323916 ofs=1613824
+ <unknown>-12683 (-----) [004] .... 1920260.584675: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1155631 ofs=1617920
+ <unknown>-12696 (-----) [006] .... 1920260.584676: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449815 ofs=1622016
+ <unknown>-12694 (-----) [005] .... 1920260.584678: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1227069 ofs=1626112
+ <unknown>-12696 (-----) [006] .... 1920260.584680: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1317692 ofs=1630208
+ <unknown>-12694 (-----) [005] .... 1920260.584681: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1492244 ofs=1634304
+ <unknown>-12683 (-----) [004] .... 1920260.584682: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241876 ofs=1638400
+ <unknown>-12697 (-----) [007] .... 1920260.585446: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1402958 ofs=167936
+ <unknown>-12697 (-----) [007] .... 1920260.585449: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1133263 ofs=172032
+ <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1295502 ofs=176128
+ <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1249495 ofs=180224
+ <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237999 ofs=184320
+ <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1280965 ofs=188416
+ <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1208361 ofs=192512
+ <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1308840 ofs=196608
+ <unknown>-12695 (-----) [004] .... 1920260.585455: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1138875 ofs=569344
+ <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314886 ofs=573440
+ <unknown>-12697 (-----) [007] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242734 ofs=200704
+ <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447386 ofs=577536
+ <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241302 ofs=204800
+ <unknown>-12695 (-----) [004] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1328663 ofs=581632
+ <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1476101 ofs=208896
+ <unknown>-12695 (-----) [004] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209461 ofs=585728
+ <unknown>-12697 (-----) [007] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080147 ofs=212992
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128509 ofs=217088
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1371915 ofs=221184
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1264015 ofs=225280
+ <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1211695 ofs=229376
+ <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1150386 ofs=233472
+ <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1135747 ofs=237568
+ <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128230 ofs=241664
+ <unknown>-12697 (-----) [007] .... 1920260.585464: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1155451 ofs=245760
+ <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1246841 ofs=249856
+ <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1462971 ofs=253952
+ <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1131333 ofs=258048
+ <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289407 ofs=262144
+ <unknown>-12695 (-----) [004] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1134730 ofs=589824
+ <unknown>-12697 (-----) [007] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289873 ofs=266240
+ <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448734 ofs=270336
+ <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1129776 ofs=593920
+ <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524090 ofs=274432
+ <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399725 ofs=598016
+ <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524081 ofs=278528
+ <unknown>-12695 (-----) [004] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1276535 ofs=602112
+ <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524060 ofs=282624
+ <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449847 ofs=606208
+ <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1158944 ofs=286720
+ <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1384536 ofs=610304
+ <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1116785 ofs=290816
+ <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1308118 ofs=614400
+ <unknown>-12697 (-----) [007] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448669 ofs=294912
+ <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1227050 ofs=618496
+ <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289324 ofs=622592
+ <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1187869 ofs=626688
+ <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1400523 ofs=630784
+ <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1344176 ofs=634880
+ <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092871 ofs=638976
+ <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092021 ofs=643072
+ <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1198169 ofs=647168
+ <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1371540 ofs=651264
+ <unknown>-12683 (-----) [005] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1195003 ofs=348160
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1228787 ofs=655360
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1236123 ofs=659456
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1137213 ofs=663552
+ <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1294618 ofs=667648
+ <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241048 ofs=671744
+ <unknown>-12695 (-----) [004] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1228779 ofs=675840
+ <unknown>-12683 (-----) [005] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1199292 ofs=352256
+ <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200861 ofs=356352
+ <unknown>-12695 (-----) [004] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1309572 ofs=679936
+ <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1215770 ofs=360448
+ <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1409002 ofs=684032
+ <unknown>-12683 (-----) [005] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1151883 ofs=364544
+ <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1103729 ofs=688128
+ <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1468126 ofs=368640
+ <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1162720 ofs=692224
+ <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1251672 ofs=372736
+ <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1199221 ofs=696320
+ <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1283325 ofs=376832
+ <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1190489 ofs=380928
+ <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1489117 ofs=385024
+ <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1273899 ofs=389120
+ <unknown>-12683 (-----) [005] .... 1920260.585485: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1274459 ofs=393216
+ <unknown>-12683 (-----) [005] .... 1920260.585486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1316649 ofs=397312
+ <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1375678 ofs=401408
+ <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1483317 ofs=405504
+ <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1240286 ofs=409600
+ <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1131345 ofs=413696
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200483 ofs=417792
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1384693 ofs=421888
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1161385 ofs=425984
+ <unknown>-12683 (-----) [005] .... 1920260.585494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1452025 ofs=430080
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1253654 ofs=434176
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1116697 ofs=438272
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1432645 ofs=442368
+ <unknown>-12694 (-----) [006] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1337397 ofs=16384
+ <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1304229 ofs=446464
+ <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1419147 ofs=450560
+ <unknown>-12683 (-----) [005] .... 1920260.585498: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1349246 ofs=454656
+ <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128519 ofs=458752
+ <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1125168 ofs=462848
+ <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1081031 ofs=20480
+ <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1293022 ofs=24576
+ <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1113007 ofs=28672
+ <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1339312 ofs=32768
+ <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1412311 ofs=36864
+ <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260960 ofs=40960
+ <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189529 ofs=45056
+ <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1412184 ofs=49152
+ <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1481227 ofs=53248
+ <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1455940 ofs=57344
+ <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299132 ofs=61440
+ <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1337375 ofs=65536
+ <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1328742 ofs=69632
+ <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1315646 ofs=73728
+ <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1225475 ofs=77824
+ <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1146097 ofs=81920
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1318775 ofs=86016
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448391 ofs=90112
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1441412 ofs=94208
+ <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1138111 ofs=98304
+ <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1143223 ofs=102400
+ <unknown>-12683 (-----) [005] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1079876 ofs=466944
+ <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447637 ofs=106496
+ <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1220585 ofs=110592
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449051 ofs=114688
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313180 ofs=118784
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313166 ofs=122880
+ <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313154 ofs=126976
+ <unknown>-12683 (-----) [005] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1218394 ofs=471040
+ <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1144047 ofs=131072
+ <unknown>-12683 (-----) [005] .... 1920260.585537: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1218579 ofs=475136
+ <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241332 ofs=135168
+ <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1097199 ofs=139264
+ <unknown>-12694 (-----) [006] .... 1920260.585545: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1214197 ofs=143360
+ <unknown>-12694 (-----) [006] .... 1920260.585645: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197633 ofs=147456
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1311536 ofs=151552
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1322952 ofs=155648
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346974 ofs=159744
+ <unknown>-12694 (-----) [006] .... 1920260.585648: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1257232 ofs=163840
+ <unknown>-12695 (-----) [004] .... 1920260.586355: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1204484 ofs=700416
+ <unknown>-12695 (-----) [004] .... 1920260.586357: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1326426 ofs=704512
+ <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1151808 ofs=708608
+ <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209422 ofs=712704
+ <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1408387 ofs=716800
+ <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197336 ofs=720896
+ <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1205652 ofs=724992
+ <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1133421 ofs=729088
+ <unknown>-12695 (-----) [004] .... 1920260.586364: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092173 ofs=733184
+ <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1124430 ofs=737280
+ <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1143926 ofs=741376
+ <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1090109 ofs=745472
+ <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1102012 ofs=749568
+ <unknown>-12695 (-----) [004] .... 1920260.586367: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1154930 ofs=753664
+ <unknown>-12695 (-----) [004] .... 1920260.586368: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1132993 ofs=757760
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1430780 ofs=761856
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197452 ofs=765952
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075111 ofs=770048
+ <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1275616 ofs=774144
+ <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1444981 ofs=778240
+ <unknown>-12695 (-----) [004] .... 1920260.586371: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1452592 ofs=782336
+ <unknown>-12695 (-----) [004] .... 1920260.586374: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1102857 ofs=786432
+ <unknown>-12695 (-----) [004] .... 1920260.586376: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406969 ofs=790528
+ <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1522553 ofs=794624
+ <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260771 ofs=798720
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1474649 ofs=802816
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1268708 ofs=806912
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346144 ofs=811008
+ <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1081167 ofs=815104
+ <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1137677 ofs=819200
+ <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1161175 ofs=823296
+ <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1461331 ofs=827392
+ <unknown>-12695 (-----) [004] .... 1920260.586492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347219 ofs=831488
+ <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1290004 ofs=835584
+ <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299174 ofs=839680
+ <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1317595 ofs=843776
+ <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1484924 ofs=847872
+ <unknown>-12695 (-----) [004] .... 1920260.586497: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1169920 ofs=851968
+ <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1359189 ofs=856064
+ <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1307842 ofs=860160
+ <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237858 ofs=864256
+ <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189461 ofs=868352
+ <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1223232 ofs=872448
+ <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1104076 ofs=876544
+ <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1079223 ofs=880640
+ <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092537 ofs=884736
+ <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1353960 ofs=888832
+ <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346330 ofs=892928
+ <unknown>-12695 (-----) [004] .... 1920260.586506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345764 ofs=897024
+ <unknown>-12695 (-----) [004] .... 1920260.586507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1363913 ofs=901120
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1319570 ofs=905216
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1367024 ofs=909312
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1333808 ofs=913408
+ <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1158627 ofs=917504
+ <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1300368 ofs=921600
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1245363 ofs=925696
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345609 ofs=929792
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1393826 ofs=933888
+ <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200552 ofs=937984
+ <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1170885 ofs=942080
+ <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1536209 ofs=946176
+ <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189630 ofs=950272
+ <unknown>-12695 (-----) [004] .... 1920260.586513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1121010 ofs=954368
+ <unknown>-12695 (-----) [004] .... 1920260.586514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324474 ofs=958464
+ <unknown>-12697 (-----) [007] .... 1920260.586578: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1129628 ofs=299008
+ <unknown>-12697 (-----) [007] .... 1920260.586579: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1307120 ofs=303104
+ <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347284 ofs=307200
+ <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1312996 ofs=311296
+ <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1170623 ofs=315392
+ <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1359281 ofs=319488
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1180021 ofs=323584
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1195728 ofs=327680
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1163642 ofs=331776
+ <unknown>-12697 (-----) [007] .... 1920260.586587: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1152538 ofs=335872
+ <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345922 ofs=339968
+ <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1343604 ofs=344064
+ <unknown>-12697 (-----) [007] .... 1920260.586721: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399371 ofs=479232
+ <unknown>-12697 (-----) [007] .... 1920260.586723: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1106549 ofs=483328
+ <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331546 ofs=487424
+ <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299299 ofs=491520
+ <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1288883 ofs=495616
+ <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399049 ofs=499712
+ <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1146931 ofs=503808
+ <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1296592 ofs=507904
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1468397 ofs=512000
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1215698 ofs=516096
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1177341 ofs=520192
+ <unknown>-12697 (-----) [007] .... 1920260.586731: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189162 ofs=524288
+ <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1435997 ofs=528384
+ <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209896 ofs=532480
+ <unknown>-12697 (-----) [007] .... 1920260.586733: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1255888 ofs=536576
+ <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1234200 ofs=540672
+ <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1422854 ofs=544768
+ <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1435794 ofs=548864
+ <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1236279 ofs=552960
+ <unknown>-12697 (-----) [007] .... 1920260.586736: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1485732 ofs=557056
+ <unknown>-12683 (-----) [005] .... 1920260.586743: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1417198 ofs=561152
+ <unknown>-12683 (-----) [005] .... 1920260.586746: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1469450 ofs=565248
+ <unknown>-12696 (-----) [004] .... 1920260.587465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1489023 ofs=1040384
+ <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449498 ofs=1044480
+ <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447737 ofs=1048576
+ <unknown>-12696 (-----) [004] .... 1920260.587470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1124530 ofs=1052672
+ <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1246743 ofs=1056768
+ <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1441927 ofs=1060864
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1280581 ofs=1064960
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289438 ofs=1069056
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1464236 ofs=1073152
+ <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1125808 ofs=1077248
+ <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1329385 ofs=1081344
+ <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314093 ofs=1085440
+ <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1201837 ofs=1089536
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1327734 ofs=1093632
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406568 ofs=1097728
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331873 ofs=1101824
+ <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331898 ofs=1105920
+ <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331917 ofs=1110016
+ <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1332091 ofs=1114112
+ <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1108186 ofs=1118208
+ <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1182631 ofs=1122304
+ <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1085941 ofs=1126400
+ <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1433982 ofs=1130496
+ <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1387028 ofs=1134592
+ <unknown>-12696 (-----) [004] .... 1920260.587488: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1353117 ofs=1138688
+ <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1352364 ofs=1142784
+ <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1144513 ofs=1146880
+ <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1403984 ofs=1150976
+ <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278970 ofs=1155072
+ <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1326743 ofs=1159168
+ <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1221809 ofs=1163264
+ <unknown>-12696 (-----) [004] .... 1920260.587492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1268668 ofs=1167360
+ <unknown>-12695 (-----) [005] .... 1920260.587502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1074544 ofs=962560
+ <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1074294 ofs=966656
+ <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075097 ofs=970752
+ <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1162407 ofs=974848
+ <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1141370 ofs=978944
+ <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306487 ofs=983040
+ <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306434 ofs=987136
+ <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306347 ofs=991232
+ <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306247 ofs=995328
+ <unknown>-12695 (-----) [005] .... 1920260.587515: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306195 ofs=999424
+ <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306039 ofs=1003520
+ <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1305983 ofs=1007616
+ <unknown>-12694 (-----) [006] .... 1920260.587701: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1216391 ofs=1171456
+ <unknown>-12694 (-----) [006] .... 1920260.587705: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1262462 ofs=1175552
+ <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1358114 ofs=1179648
+ <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1357898 ofs=1183744
+ <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237003 ofs=1187840
+ <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1126319 ofs=1191936
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1415489 ofs=1196032
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279558 ofs=1200128
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1434022 ofs=1204224
+ <unknown>-12694 (-----) [006] .... 1920260.587709: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1220130 ofs=1208320
+ <unknown>-12694 (-----) [006] .... 1920260.587710: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1163037 ofs=1212416
+ <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1404501 ofs=1216512
+ <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406287 ofs=1220608
+ <unknown>-12697 (-----) [007] .... 1920260.588132: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1355143 ofs=1376256
+ <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1213923 ofs=1380352
+ <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1243190 ofs=1384448
+ <unknown>-12697 (-----) [007] .... 1920260.588143: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1300698 ofs=1388544
+ <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1482568 ofs=1392640
+ <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1461789 ofs=1396736
+ <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242314 ofs=1400832
+ <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1471996 ofs=1404928
+ <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242742 ofs=1409024
+ <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242579 ofs=1413120
+ <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242553 ofs=1417216
+ <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1457332 ofs=1421312
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1315431 ofs=1425408
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080653 ofs=1429504
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324174 ofs=1433600
+ <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324142 ofs=1437696
+ <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1157760 ofs=1441792
+ <unknown>-12697 (-----) [007] .... 1920260.588151: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075059 ofs=1445888
+ <unknown>-12683 (-----) [006] .... 1920260.589785: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279192 ofs=1486848
+ <unknown>-12683 (-----) [006] .... 1920260.589790: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278527 ofs=1490944
+ <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1091778 ofs=1495040
+ <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1339447 ofs=1499136
+ <unknown>-12683 (-----) [006] .... 1920260.589792: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1254007 ofs=1503232
+ <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1115173 ofs=1507328
+ <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1393985 ofs=1511424
+ <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1369123 ofs=1515520
+ <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314257 ofs=1519616
+ <unknown>-12683 (-----) [006] .... 1920260.589802: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1404487 ofs=1523712
+ <unknown>-12683 (-----) [006] .... 1920260.589803: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1354554 ofs=1527808
+ <unknown>-12683 (-----) [006] .... 1920260.594312: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1141445 ofs=9801728
+ <unknown>-12683 (-----) [006] .... 1920260.594322: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323774 ofs=231460864
+ <unknown>-12683 (-----) [006] .... 1920260.594326: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323772 ofs=10993664
+ <unknown>-12683 (-----) [006] .... 1920260.595212: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481305 ofs=9805824
+ <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481306 ofs=9809920
+ <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481316 ofs=9814016
+ <unknown>-12683 (-----) [006] .... 1920260.595215: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481340 ofs=9818112
+ <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1394587 ofs=9822208
+ <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103455 ofs=9826304
+ <unknown>-12683 (-----) [006] .... 1920260.595217: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103271 ofs=9830400
+ <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103168 ofs=9834496
+ <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103145 ofs=9838592
+ <unknown>-12683 (-----) [006] .... 1920260.595219: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103115 ofs=9842688
+ <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103057 ofs=9846784
+ <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1331958 ofs=9850880
+ <unknown>-12683 (-----) [006] .... 1920260.595227: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1356305 ofs=9854976
+ <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103708 ofs=9859072
+ <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1099286 ofs=9863168
+ <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435190 ofs=9867264
+ <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1395504 ofs=9871360
+ <unknown>-12683 (-----) [006] .... 1920260.595230: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1352916 ofs=9875456
+ <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1255529 ofs=9879552
+ <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1336145 ofs=9883648
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1334143 ofs=9887744
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1328548 ofs=9891840
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1222215 ofs=9895936
+ <unknown>-12683 (-----) [006] .... 1920260.595233: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1461056 ofs=9900032
+ <unknown>-12683 (-----) [006] .... 1920260.595234: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1228276 ofs=9904128
+ <unknown>-12683 (-----) [006] .... 1920260.595235: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1151188 ofs=9908224
+ <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1443605 ofs=9912320
+ <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1146821 ofs=9916416
+ <unknown>-12683 (-----) [006] .... 1920260.595237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103669 ofs=9920512
+ <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103744 ofs=9924608
+ <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103868 ofs=9928704
+ <unknown>-12683 (-----) [006] .... 1920260.595789: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1465942 ofs=15855616
+ <unknown>-12683 (-----) [006] .... 1920260.595792: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323712 ofs=261189632
+ <unknown>-12683 (-----) [006] .... 1920260.595998: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323701 ofs=262094848
+ <unknown>-12683 (-----) [006] .... 1920260.596191: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1222287 ofs=15859712
+ <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1213146 ofs=15863808
+ <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1310396 ofs=15867904
+ <unknown>-12683 (-----) [006] .... 1920260.596193: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1310177 ofs=15872000
+ <unknown>-12683 (-----) [006] .... 1920260.596194: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1187914 ofs=15876096
+ <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1322409 ofs=15880192
+ <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1282484 ofs=15884288
+ <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1097245 ofs=15888384
+ <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1416816 ofs=15892480
+ <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1257125 ofs=15896576
+ <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1403527 ofs=15900672
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1218006 ofs=15904768
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1153893 ofs=15908864
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1328023 ofs=15912960
+ <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1465412 ofs=15917056
+ <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1092448 ofs=15921152
+ <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1239220 ofs=15925248
+ <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1276491 ofs=15929344
+ <unknown>-12683 (-----) [006] .... 1920260.596205: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1262240 ofs=15933440
+ <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1323793 ofs=15937536
+ <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1074937 ofs=15941632
+ <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1311157 ofs=15945728
+ <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1308442 ofs=15949824
+ <unknown>-12683 (-----) [006] .... 1920260.596210: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1467709 ofs=15953920
+ <unknown>-12683 (-----) [006] .... 1920260.596211: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1394299 ofs=15958016
+ <unknown>-12683 (-----) [004] .... 1920260.612586: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1316156 ofs=344064
+ <unknown>-12683 (-----) [004] .... 1920260.612591: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1406323 ofs=348160
+ <unknown>-12683 (-----) [004] .... 1920260.612601: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1216972 ofs=352256
+ <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1271924 ofs=356352
+ <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1369225 ofs=360448
+ <unknown>-12683 (-----) [004] .... 1920260.612608: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1318474 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.612609: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1227283 ofs=368640
+ <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1364376 ofs=372736
+ <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1073400 ofs=376832
diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb b/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb
new file mode 100644
index 0000000..ab3df45
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb
Binary files differ
diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb b/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb
new file mode 100644
index 0000000..17cb116
--- /dev/null
+++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb
Binary files differ
diff --git a/startop/scripts/lib/cmd_utils.py b/startop/scripts/lib/cmd_utils.py
new file mode 100644
index 0000000..6071f14
--- /dev/null
+++ b/startop/scripts/lib/cmd_utils.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Helper util libraries for command line operations."""
+
+import asyncio
+import sys
+import time
+from typing import Tuple, Optional, List
+
+import lib.print_utils as print_utils
+
+TIMEOUT = 50
+SIMULATE = False
+
+def run_command_nofail(cmd: List[str], **kwargs) -> None:
+ """Runs cmd list with default timeout.
+
+ Throws exception if the execution fails.
+ """
+ my_kwargs = {"timeout": TIMEOUT, "shell": False, "simulate": False}
+ my_kwargs.update(kwargs)
+ passed, out = execute_arbitrary_command(cmd, **my_kwargs)
+ if not passed:
+ raise RuntimeError(
+ "Failed to execute %s (kwargs=%s), output=%s" % (cmd, kwargs, out))
+
+def run_adb_shell_command(cmd: str) -> Tuple[bool, str]:
+ """Runs command using adb shell.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed).
+ """
+ return run_shell_command('adb shell "{}"'.format(cmd))
+
+def run_shell_func(script_path: str,
+ func: str,
+ args: List[str]) -> Tuple[bool, str]:
+ """Runs shell function with default timeout.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ if args:
+ cmd = 'bash -c "source {script_path}; {func} {args}"'.format(
+ script_path=script_path,
+ func=func,
+ args=' '.join("'{}'".format(arg) for arg in args))
+ else:
+ cmd = 'bash -c "source {script_path}; {func}"'.format(
+ script_path=script_path,
+ func=func)
+
+ print_utils.debug_print(cmd)
+ return run_shell_command(cmd)
+
+def run_shell_command(cmd: str) -> Tuple[bool, str]:
+ """Runs shell command with default timeout.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ return execute_arbitrary_command([cmd],
+ TIMEOUT,
+ shell=True,
+ simulate=SIMULATE)
+
+def execute_arbitrary_command(cmd: List[str],
+ timeout: int,
+ shell: bool,
+ simulate: bool) -> Tuple[bool, str]:
+ """Run arbitrary shell command with default timeout.
+
+ Mostly copy from
+ frameworks/base/startop/scripts/app_startup/app_startup_runner.py.
+
+ Args:
+ cmd: list of cmd strings.
+ timeout: the time limit of running cmd.
+ shell: indicate if the cmd is a shell command.
+ simulate: if it's true, do not run the command and assume the running is
+ successful.
+
+ Returns:
+ A tuple of running status (True=succeeded, False=failed or timed out) and
+ std output (string contents of stdout with trailing whitespace removed) .
+ """
+ if simulate:
+ print(cmd)
+ return True, ''
+
+ print_utils.debug_print('[EXECUTE]', cmd)
+ # block until either command finishes or the timeout occurs.
+ loop = asyncio.get_event_loop()
+
+ (return_code, script_output) = loop.run_until_complete(
+ _run_command(*cmd, shell=shell, timeout=timeout))
+
+ script_output = script_output.decode() # convert bytes to str
+
+ passed = (return_code == 0)
+ print_utils.debug_print('[$?]', return_code)
+ if not passed:
+ print('[FAILED, code:%s]' % (return_code), script_output, file=sys.stderr)
+
+ return passed, script_output.rstrip()
+
+async def _run_command(*args: List[str],
+ shell: bool = False,
+ timeout: Optional[int] = None) -> Tuple[int, bytes]:
+ if shell:
+ process = await asyncio.create_subprocess_shell(
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
+ else:
+ process = await asyncio.create_subprocess_exec(
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
+
+ script_output = b''
+
+ print_utils.debug_print('[PID]', process.pid)
+
+ timeout_remaining = timeout
+ time_started = time.time()
+
+ # read line (sequence of bytes ending with b'\n') asynchronously
+ while True:
+ try:
+ line = await asyncio.wait_for(process.stdout.readline(),
+ timeout_remaining)
+ print_utils.debug_print('[STDOUT]', line)
+ script_output += line
+
+ if timeout_remaining:
+ time_elapsed = time.time() - time_started
+ timeout_remaining = timeout - time_elapsed
+ except asyncio.TimeoutError:
+ print_utils.debug_print('[TIMEDOUT] Process ', process.pid)
+
+ print_utils.debug_print('[TIMEDOUT] Sending SIGTERM.')
+ process.terminate()
+
+ # 5 second timeout for process to handle SIGTERM nicely.
+ try:
+ (remaining_stdout,
+ remaining_stderr) = await asyncio.wait_for(process.communicate(), 5)
+ script_output += remaining_stdout
+ except asyncio.TimeoutError:
+ print_utils.debug_print('[TIMEDOUT] Sending SIGKILL.')
+ process.kill()
+
+ # 5 second timeout to finish with SIGKILL.
+ try:
+ (remaining_stdout,
+ remaining_stderr) = await asyncio.wait_for(process.communicate(), 5)
+ script_output += remaining_stdout
+ except asyncio.TimeoutError:
+ # give up, this will leave a zombie process.
+ print_utils.debug_print('[TIMEDOUT] SIGKILL failed for process ',
+ process.pid)
+ time.sleep(100)
+
+ return -1, script_output
+ else:
+ if not line: # EOF
+ break
+
+ code = await process.wait() # wait for child process to exit
+ return code, script_output
diff --git a/startop/scripts/lib/logcat_utils.py b/startop/scripts/lib/logcat_utils.py
new file mode 100644
index 0000000..8a3d00b
--- /dev/null
+++ b/startop/scripts/lib/logcat_utils.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Helper util libraries for parsing logcat logs."""
+
+import asyncio
+import re
+from datetime import datetime
+from typing import Optional, Pattern
+
+# local import
+import lib.print_utils as print_utils
+
+def parse_logcat_datetime(timestamp: str) -> Optional[datetime]:
+ """Parses the timestamp of logcat.
+
+ Params:
+ timestamp: for example "2019-07-01 16:13:55.221".
+
+ Returns:
+ a datetime of timestamp with the year now.
+ """
+ try:
+ # Match the format of logcat. For example: "2019-07-01 16:13:55.221",
+ # because it doesn't have year, set current year to it.
+ timestamp = datetime.strptime(timestamp,
+ '%Y-%m-%d %H:%M:%S.%f')
+ return timestamp
+ except ValueError as ve:
+ print_utils.debug_print('Invalid line: ' + timestamp)
+ return None
+
+def _is_time_out(timeout: datetime, line: str) -> bool:
+ """Checks if the timestamp of this line exceeds the timeout.
+
+ Returns:
+ true if the timestamp exceeds the timeout.
+ """
+ # Get the timestampe string.
+ cur_timestamp_str = ' '.join(re.split(r'\s+', line)[0:2])
+ timestamp = parse_logcat_datetime(cur_timestamp_str)
+ if not timestamp:
+ return False
+
+ return timestamp > timeout
+
+async def _blocking_wait_for_logcat_pattern(timestamp: datetime,
+ pattern: Pattern,
+ timeout: datetime) -> Optional[str]:
+ # Show the year in the timestampe.
+ logcat_cmd = 'adb logcat -v UTC -v year -v threadtime -T'.split()
+ logcat_cmd.append(str(timestamp))
+ print_utils.debug_print('[LOGCAT]:' + ' '.join(logcat_cmd))
+
+ # Create subprocess
+ process = await asyncio.create_subprocess_exec(
+ *logcat_cmd,
+ # stdout must a pipe to be accessible as process.stdout
+ stdout=asyncio.subprocess.PIPE)
+
+ while (True):
+ # Read one line of output.
+ data = await process.stdout.readline()
+ line = data.decode('utf-8').rstrip()
+
+ # 2019-07-01 14:54:21.946 27365 27392 I ActivityTaskManager: Displayed
+ # com.android.settings/.Settings: +927ms
+ # TODO: Detect timeouts even when there is no logcat output.
+ if _is_time_out(timeout, line):
+ print_utils.debug_print('DID TIMEOUT BEFORE SEEING ANYTHING ('
+ 'timeout={timeout} seconds << {pattern} '
+ '>>'.format(timeout=timeout, pattern=pattern))
+ return None
+
+ if pattern.match(line):
+ print_utils.debug_print(
+ 'WE DID SEE PATTERN << "{}" >>.'.format(pattern))
+ return line
+
+def blocking_wait_for_logcat_pattern(timestamp: datetime,
+ pattern: Pattern,
+ timeout: datetime) -> Optional[str]:
+ """Selects the line that matches the pattern and within the timeout.
+
+ Returns:
+ the line that matches the pattern and within the timeout.
+ """
+ loop = asyncio.get_event_loop()
+ result = loop.run_until_complete(
+ _blocking_wait_for_logcat_pattern(timestamp, pattern, timeout))
+ return result
diff --git a/startop/scripts/lib/logcat_utils_test.py b/startop/scripts/lib/logcat_utils_test.py
new file mode 100644
index 0000000..ab82515
--- /dev/null
+++ b/startop/scripts/lib/logcat_utils_test.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+"""Unit tests for the logcat_utils.py script."""
+
+import asyncio
+import datetime
+import re
+
+import logcat_utils
+from mock import MagicMock, patch
+
+def test_parse_logcat_datatime():
+ # Act
+ result = logcat_utils.parse_logcat_datetime('2019-07-01 16:13:55.221')
+
+ # Assert
+ assert result == datetime.datetime(2019, 7, 1, 16, 13, 55, 221000)
+
+class AsyncMock(MagicMock):
+ async def __call__(self, *args, **kwargs):
+ return super(AsyncMock, self).__call__(*args, **kwargs)
+
+def _async_return():
+ f = asyncio.Future()
+ f.set_result(
+ b'2019-07-01 15:51:53.290 27365 27392 I ActivityTaskManager: '
+ b'Displayed com.google.android.music/com.android.music.activitymanagement.'
+ b'TopLevelActivity: +1s7ms')
+ return f
+
+def test_parse_displayed_time_succeed():
+ # Act
+ with patch('asyncio.create_subprocess_exec',
+ new_callable=AsyncMock) as asyncio_mock:
+ asyncio_mock.return_value.stdout.readline = _async_return
+ timestamp = datetime.datetime(datetime.datetime.now().year, 7, 1, 16, 13,
+ 55, 221000)
+ timeout_dt = timestamp + datetime.timedelta(0, 10)
+ pattern = re.compile('.*ActivityTaskManager: Displayed '
+ 'com.google.android.music/com.android.music.*')
+ result = logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
+ pattern,
+ timeout_dt)
+
+ # Assert
+ assert result == '2019-07-01 15:51:53.290 27365 27392 I ' \
+ 'ActivityTaskManager: ' \
+ 'Displayed com.google.android.music/com.android.music.' \
+ 'activitymanagement.TopLevelActivity: +1s7ms'
+
+def _async_timeout_return():
+ f = asyncio.Future()
+ f.set_result(
+ b'2019-07-01 17:51:53.290 27365 27392 I ActivityTaskManager: '
+ b'Displayed com.google.android.music/com.android.music.activitymanagement.'
+ b'TopLevelActivity: +1s7ms')
+ return f
+
+def test_parse_displayed_time_timeout():
+ # Act
+ with patch('asyncio.create_subprocess_exec',
+ new_callable=AsyncMock) as asyncio_mock:
+ asyncio_mock.return_value.stdout.readline = _async_timeout_return
+ timestamp = datetime.datetime(datetime.datetime.now().year,
+ 7, 1, 16, 13, 55, 221000)
+ timeout_dt = timestamp + datetime.timedelta(0, 10)
+ pattern = re.compile('.*ActivityTaskManager: Displayed '
+ 'com.google.android.music/com.android.music.*')
+ result = logcat_utils.blocking_wait_for_logcat_pattern(timestamp,
+ pattern,
+ timeout_dt)
+
+ # Assert
+ assert result == None
diff --git a/startop/scripts/lib/print_utils.py b/startop/scripts/lib/print_utils.py
new file mode 100644
index 0000000..8c5999d
--- /dev/null
+++ b/startop/scripts/lib/print_utils.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+"""Helper util libraries for debug printing."""
+
+import sys
+
+DEBUG = False
+
+def debug_print(*args, **kwargs):
+ """Prints the args to sys.stderr if the DEBUG is set."""
+ if DEBUG:
+ print(*args, **kwargs, file=sys.stderr)
+
+def error_print(*args, **kwargs):
+ print('[ERROR]:', *args, file=sys.stderr, **kwargs)
+
+def _expand_gen_repr(args):
+ """Like repr but any generator-like object has its iterator consumed
+ and then called repr on."""
+ new_args_list = []
+ for i in args:
+ # detect iterable objects that do not have their own override of __str__
+ if hasattr(i, '__iter__'):
+ to_str = getattr(i, '__str__')
+ if to_str.__objclass__ == object:
+ # the repr for a generator is just type+address, expand it out instead.
+ new_args_list.append([_expand_gen_repr([j])[0] for j in i])
+ continue
+ # normal case: uses the built-in to-string
+ new_args_list.append(i)
+ return new_args_list
+
+def debug_print_gen(*args, **kwargs):
+ """Like _debug_print but will turn any iterable args into a list."""
+ if not DEBUG:
+ return
+
+ new_args_list = _expand_gen_repr(args)
+ debug_print(*new_args_list, **kwargs)
+
+def debug_print_nd(*args, **kwargs):
+ """Like _debug_print but will turn any NamedTuple-type args into a string."""
+ if not DEBUG:
+ return
+
+ new_args_list = []
+ for i in args:
+ if hasattr(i, '_field_types'):
+ new_args_list.append("%s: %s" % (i.__name__, i._field_types))
+ else:
+ new_args_list.append(i)
+
+ debug_print(*new_args_list, **kwargs)
diff --git a/startop/scripts/trace_analyzer/lib/trace2db.py b/startop/scripts/trace_analyzer/lib/trace2db.py
new file mode 100644
index 0000000..42a33af
--- /dev/null
+++ b/startop/scripts/trace_analyzer/lib/trace2db.py
@@ -0,0 +1,355 @@
+#!/usr/bin/python3
+# 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.
+
+import re
+import sys
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column, Date, Integer, Float, String, ForeignKey
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import relationship
+
+from sqlalchemy.orm import sessionmaker
+
+import sqlalchemy
+
+from typing import Optional, Tuple
+
+_DEBUG = False # print sql commands to console
+_FLUSH_LIMIT = 10000 # how many entries are parsed before flushing to DB from memory
+
+Base = declarative_base()
+
+class RawFtraceEntry(Base):
+ __tablename__ = 'raw_ftrace_entries'
+
+ id = Column(Integer, primary_key=True)
+ task_name = Column(String, nullable=True) # <...> -> None.
+ task_pid = Column(String, nullable=False)
+ tgid = Column(Integer, nullable=True) # ----- -> None.
+ cpu = Column(Integer, nullable=False)
+ timestamp = Column(Float, nullable=False)
+ function = Column(String, nullable=False)
+ function_args = Column(String, nullable=False)
+
+ # 1:1 relation with MmFilemapAddToPageCache.
+ mm_filemap_add_to_page_cache = relationship("MmFilemapAddToPageCache",
+ back_populates="raw_ftrace_entry")
+
+ @staticmethod
+ def parse_dict(line):
+ # ' <...>-5521 (-----) [003] ...1 17148.446877: tracing_mark_write: trace_event_clock_sync: parent_ts=17148.447266'
+ m = re.match('\s*(.*)-(\d+)\s+\(([^\)]+)\)\s+\[(\d+)\]\s+([\w.]{4})\s+(\d+[.]\d+):\s+(\w+):\s+(.*)', line)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # groups example:
+ # ('<...>',
+ # '5521',
+ # '-----',
+ # '003',
+ # '...1',
+ # '17148.446877',
+ # 'tracing_mark_write',
+ # 'trace_event_clock_sync: parent_ts=17148.447266')
+ task_name = groups[0]
+ if task_name == '<...>':
+ task_name = None
+
+ task_pid = int(groups[1])
+ tgid = groups[2]
+ if tgid == '-----':
+ tgid = None
+
+ cpu = int(groups[3])
+ # irq_flags = groups[4]
+ timestamp = float(groups[5])
+ function = groups[6]
+ function_args = groups[7]
+
+ return {'task_name': task_name, 'task_pid': task_pid, 'tgid': tgid, 'cpu': cpu, 'timestamp': timestamp, 'function': function, 'function_args': function_args}
+
+class SchedSwitch(Base):
+ __tablename__ = 'sched_switches'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ prev_comm = Column(String, nullable=False)
+ prev_pid = Column(Integer, nullable=False)
+ prev_prio = Column(Integer, nullable=False)
+ prev_state = Column(String, nullable=False)
+
+ next_comm = Column(String, nullable=False)
+ next_pid = Column(Integer, nullable=False)
+ next_prio = Column(Integer, nullable=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # 'prev_comm=kworker/u16:5 prev_pid=13971 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120'
+ m = re.match("prev_comm=(.*) prev_pid=(\d+) prev_prio=(\d+) prev_state=(.*) ==> next_comm=(.*) next_pid=(\d+) next_prio=(\d+) ?", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('kworker/u16:5', '13971', '120', 'S', 'swapper/4', '0', '120')
+ d = {}
+ if id is not None:
+ d['id'] = id
+ d['prev_comm'] = groups[0]
+ d['prev_pid'] = int(groups[1])
+ d['prev_prio'] = int(groups[2])
+ d['prev_state'] = groups[3]
+ d['next_comm'] = groups[4]
+ d['next_pid'] = int(groups[5])
+ d['next_prio'] = int(groups[6])
+
+ return d
+
+class SchedBlockedReason(Base):
+ __tablename__ = 'sched_blocked_reasons'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ pid = Column(Integer, nullable=False)
+ iowait = Column(Integer, nullable=False)
+ caller = Column(String, nullable=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # 'pid=2289 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f'
+ m = re.match("pid=(\d+) iowait=(\d+) caller=(.*) ?", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('2289', '1', 'wait_on_page_bit_common+0x2a8/0x5f8')
+ d = {}
+ if id is not None:
+ d['id'] = id
+ d['pid'] = int(groups[0])
+ d['iowait'] = int(groups[1])
+ d['caller'] = groups[2]
+
+ return d
+
+class MmFilemapAddToPageCache(Base):
+ __tablename__ = 'mm_filemap_add_to_page_caches'
+
+ id = Column(Integer, ForeignKey('raw_ftrace_entries.id'), primary_key=True)
+
+ dev = Column(Integer, nullable=False) # decoded from ${major}:${minor} syntax.
+ dev_major = Column(Integer, nullable=False) # original ${major} value.
+ dev_minor = Column(Integer, nullable=False) # original ${minor} value.
+
+ ino = Column(Integer, nullable=False) # decoded from hex to base 10
+ page = Column(Integer, nullable=False) # decoded from hex to base 10
+
+ pfn = Column(Integer, nullable=False)
+ ofs = Column(Integer, nullable=False)
+
+ # 1:1 relation with RawFtraceEntry.
+ raw_ftrace_entry = relationship("RawFtraceEntry", uselist=False)
+
+ @staticmethod
+ def parse_dict(function_args, id = None):
+ # dev 253:6 ino b2c7 page=00000000ec787cd9 pfn=1478539 ofs=4096
+ m = re.match("dev (\d+):(\d+) ino ([0-9a-fA-F]+) page=([0-9a-fA-F]+) pfn=(\d+) ofs=(\d+)", function_args)
+ if not m:
+ return None
+
+ groups = m.groups()
+ # ('253', '6', 'b2c7', '00000000ec787cd9', '1478539', '4096')
+ d = {}
+ if id is not None:
+ d['id'] = id
+
+ device_major = d['dev_major'] = int(groups[0])
+ device_minor = d['dev_minor'] = int(groups[1])
+ d['dev'] = device_major << 8 | device_minor
+ d['ino'] = int(groups[2], 16)
+ d['page'] = int(groups[3], 16)
+ d['pfn'] = int(groups[4])
+ d['ofs'] = int(groups[5])
+
+ return d
+
+class Trace2Db:
+ def __init__(self, db_filename: str):
+ (s, e) = self._init_sqlalchemy(db_filename)
+ self._session = s
+ self._engine = e
+ self._raw_ftrace_entry_filter = lambda x: True
+
+ def set_raw_ftrace_entry_filter(self, flt):
+ """
+ Install a function dict(RawFtraceEntry) -> bool
+
+ If this returns 'false', then we skip adding the RawFtraceEntry to the database.
+ """
+ self._raw_ftrace_entry_filter = flt
+
+ @staticmethod
+ def _init_sqlalchemy(db_filename: str) -> Tuple[object, object]:
+ global _DEBUG
+ engine = create_engine('sqlite:///' + db_filename, echo=_DEBUG)
+
+ # CREATE ... (tables)
+ Base.metadata.create_all(engine)
+
+ Session = sessionmaker(bind=engine)
+ session = Session()
+ return (session, engine)
+
+ def parse_file_into_db(self, filename: str, limit: Optional[int] = None):
+ """
+ Parse the ftrace/systrace at 'filename',
+ inserting the values into the current sqlite database.
+
+ :return: number of RawFtraceEntry inserted.
+ """
+ return parse_file(filename, self._session, self._engine, self._raw_ftrace_entry_filter, limit)
+
+ def parse_file_buf_into_db(self, file_buf, limit: Optional[int] = None):
+ """
+ Parse the ftrace/systrace at 'filename',
+ inserting the values into the current sqlite database.
+
+ :return: number of RawFtraceEntry inserted.
+ """
+ return parse_file_buf(file_buf, self._session, self._engine, self._raw_ftrace_entry_filter, limit)
+
+
+ @property
+ def session(self):
+ return self._session
+
+def insert_pending_entries(engine, kls, lst):
+ if len(lst) > 0:
+ # for some reason, it tries to generate an empty INSERT statement with len=0,
+ # which of course violates the first non-null constraint.
+ try:
+ # Performance-sensitive parsing according to:
+ # https://docs.sqlalchemy.org/en/13/faq/performance.html#i-m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow
+ engine.execute(kls.__table__.insert(), lst)
+ lst.clear()
+ except sqlalchemy.exc.IntegrityError as err:
+ # possibly violating some SQL constraint, print data here.
+ print(err)
+ print(lst)
+ raise
+
+def parse_file(filename: str, *args, **kwargs) -> int:
+ # use explicit encoding to avoid UnicodeDecodeError.
+ with open(filename, encoding="ISO-8859-1") as f:
+ return parse_file_buf(f, *args, **kwargs)
+
+def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None) -> int:
+ global _FLUSH_LIMIT
+ count = 0
+ # count and id are not equal, because count still increases for invalid lines.
+ id = 0
+
+ pending_entries = []
+ pending_sched_switch = []
+ pending_sched_blocked_reasons = []
+ pending_mm_filemap_add_to_pagecaches = []
+
+ def insert_all_pending_entries():
+ insert_pending_entries(engine, RawFtraceEntry, pending_entries)
+ insert_pending_entries(engine, SchedSwitch, pending_sched_switch)
+ insert_pending_entries(engine, SchedBlockedReason, pending_sched_blocked_reasons)
+ insert_pending_entries(engine, MmFilemapAddToPageCache, pending_mm_filemap_add_to_pagecaches)
+
+ # for trace.html files produced by systrace,
+ # the actual ftrace is in the 'second' trace-data script class.
+ parsing_trace_data = 0
+ parsing_systrace_file = False
+
+ f = filebuf
+ for l in f:
+ if parsing_trace_data == 0 and l == "<!DOCTYPE html>\n":
+ parsing_systrace_file = True
+ continue
+ if parsing_trace_data != 2 and parsing_systrace_file:
+ if l == ' <script class="trace-data" type="application/text">\n':
+ parsing_trace_data = parsing_trace_data + 1
+ continue
+
+ if parsing_systrace_file and parsing_trace_data != 2:
+ continue
+ elif parsing_systrace_file and parsing_trace_data == 2 and l == " </script>\n":
+ # the rest of this file is just random html
+ break
+
+ # now parsing the ftrace data.
+ if len(l) > 1 and l[0] == '#':
+ continue
+
+ count = count + 1
+
+ if limit and count >= limit:
+ break
+
+ raw_ftrace_entry = RawFtraceEntry.parse_dict(l)
+ if not raw_ftrace_entry:
+ print("WARNING: Failed to parse raw ftrace entry: " + l)
+ continue
+
+ if not raw_ftrace_entry_filter(raw_ftrace_entry):
+ # Skip processing raw ftrace entries that don't match a filter.
+ # This is an optimization for when Trace2Db is used programatically
+ # to avoid having an overly large database.
+ continue
+
+ pending_entries.append(raw_ftrace_entry)
+ id = id + 1
+
+ if raw_ftrace_entry['function'] == 'sched_switch':
+ sched_switch = SchedSwitch.parse_dict(raw_ftrace_entry['function_args'], id)
+
+ if not sched_switch:
+ print("WARNING: Failed to parse sched_switch: " + l)
+ else:
+ pending_sched_switch.append(sched_switch)
+
+ elif raw_ftrace_entry['function'] == 'sched_blocked_reason':
+ sbr = SchedBlockedReason.parse_dict(raw_ftrace_entry['function_args'], id)
+
+ if not sbr:
+ print("WARNING: Failed to parse sched_blocked_reason: " + l)
+ else:
+ pending_sched_blocked_reasons.append(sbr)
+
+ elif raw_ftrace_entry['function'] == 'mm_filemap_add_to_page_cache':
+ d = MmFilemapAddToPageCache.parse_dict(raw_ftrace_entry['function_args'],
+ id)
+ if not d:
+ print("WARNING: Failed to parse mm_filemap_add_to_page_cache: " + l)
+ else:
+ pending_mm_filemap_add_to_pagecaches.append(d)
+
+ # Objects are cached in python memory, not yet sent to SQL database.
+
+ # Send INSERT/UPDATE/etc statements to the underlying SQL database.
+ if count % _FLUSH_LIMIT == 0:
+ insert_all_pending_entries()
+
+ insert_all_pending_entries()
+
+ # Ensure underlying database commits changes from memory to disk.
+ session.commit()
+
+ return count
diff --git a/startop/scripts/trace_analyzer/lib/trace2db_test.py b/startop/scripts/trace_analyzer/lib/trace2db_test.py
new file mode 100755
index 0000000..3b326f0
--- /dev/null
+++ b/startop/scripts/trace_analyzer/lib/trace2db_test.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+
+"""
+Unit tests for inode2filename module.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> ./inode2filename_test.py
+ $> pytest inode2filename_test.py
+ $> python -m pytest inode2filename_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+import io
+from copy import deepcopy
+
+# pip imports
+# local imports
+from trace2db import *
+
+# This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
+class EqualsSqlAlchemyObject:
+ # For convenience to write shorter tests, we also add 'ignore_fields' which allow us to specify
+ # which fields to ignore when doing the comparison.
+ def __init__(self_, self, ignore_fields=[]):
+ self_.self = self
+ self_.ignore_fields = ignore_fields
+
+ # Do field-by-field comparison.
+ # It seems that SQLAlchemy does not implement __eq__ itself so we have to do it ourselves.
+ def __eq__(self_, other):
+ if isinstance(other, EqualsSqlAlchemyObject):
+ other = other.self
+
+ self = self_.self
+
+ classes_match = isinstance(other, self.__class__)
+ a, b = deepcopy(self.__dict__), deepcopy(other.__dict__)
+
+ #compare based on equality our attributes, ignoring SQLAlchemy internal stuff
+
+ a.pop('_sa_instance_state', None)
+ b.pop('_sa_instance_state', None)
+
+ for f in self_.ignore_fields:
+ a.pop(f, None)
+ b.pop(f, None)
+
+ attrs_match = (a == b)
+ return classes_match and attrs_match
+
+ def __repr__(self):
+ return repr(self.self.__dict__)
+
+
+def assert_eq_ignore_id(left, right):
+ # This pretty-prints the raw dictionary of the sqlalchemy object if it fails.
+ # It does field-by-field comparison, but ignores the 'id' field.
+ assert EqualsSqlAlchemyObject(left, ignore_fields=['id']) == EqualsSqlAlchemyObject(right)
+
+def parse_trace_file_to_db(*contents):
+ """
+ Make temporary in-memory sqlite3 database by parsing the string contents as a trace.
+
+ :return: Trace2Db instance
+ """
+ buf = io.StringIO()
+
+ for c in contents:
+ buf.write(c)
+ buf.write("\n")
+
+ buf.seek(0)
+
+ t2d = Trace2Db(":memory:")
+ t2d.parse_file_buf_into_db(buf)
+
+ buf.close()
+
+ return t2d
+
+def test_ftrace_mm_filemap_add_to_pagecache():
+ test_contents = """
+MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+MediaStoreImpor-27212 (27176) [000] .... 16136.595920: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000048e2e156 pfn=677645 ofs=126976
+MediaStoreImpor-27212 (27176) [000] .... 16136.597793: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000051eabfb2 pfn=677644 ofs=122880
+MediaStoreImpor-27212 (27176) [000] .... 16136.597815: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000ce7cd606 pfn=677643 ofs=131072
+MediaStoreImpor-27212 (27176) [000] .... 16136.603732: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=000000008ffd3030 pfn=730119 ofs=186482688
+MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0
+ <...>-27197 (-----) [002] .... 16136.613471: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000aca88a97 pfn=743346 ofs=241664
+ <...>-27197 (-----) [002] .... 16136.615979: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000351f2bc1 pfn=777799 ofs=106496
+ <...>-27224 (-----) [006] .... 16137.400090: mm_filemap_add_to_page_cache: dev 253:6 ino 712d page=000000006ff7ffdb pfn=754861 ofs=0
+ <...>-1396 (-----) [000] .... 16137.451660: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000ba0cbb34 pfn=769173 ofs=187191296
+ <...>-1396 (-----) [000] .... 16137.453020: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000f6ef038e pfn=820291 ofs=0
+ <...>-1396 (-----) [000] .... 16137.453067: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000083ebc446 pfn=956463 ofs=4096
+ <...>-1396 (-----) [000] .... 16137.453101: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009dc2cd25 pfn=822813 ofs=8192
+ <...>-1396 (-----) [000] .... 16137.453113: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000a11167fb pfn=928650 ofs=12288
+ <...>-1396 (-----) [000] .... 16137.453126: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000c1c3311b pfn=621110 ofs=16384
+ <...>-1396 (-----) [000] .... 16137.453139: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000009aa78342 pfn=689370 ofs=20480
+ <...>-1396 (-----) [000] .... 16137.453151: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=0000000082cddcd6 pfn=755584 ofs=24576
+ <...>-1396 (-----) [000] .... 16137.453162: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=00000000b0249bc7 pfn=691431 ofs=28672
+ <...>-1396 (-----) [000] .... 16137.453183: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000006a776ff0 pfn=795084 ofs=32768
+ <...>-1396 (-----) [000] .... 16137.453203: mm_filemap_add_to_page_cache: dev 253:6 ino b285 page=000000001a4918a7 pfn=806998 ofs=36864
+ <...>-2578 (-----) [002] .... 16137.561871: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=187015168
+ <...>-2578 (-----) [002] .... 16137.562846: mm_filemap_add_to_page_cache: dev 253:6 ino b25a page=000000002f6ba74f pfn=864982 ofs=0
+ <...>-2578 (-----) [000] .... 16138.104500: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000f888d0f6 pfn=805812 ofs=192794624
+ <...>-2578 (-----) [000] .... 16138.105836: mm_filemap_add_to_page_cache: dev 253:6 ino b7dd page=000000003749523b pfn=977196 ofs=0
+ <...>-27215 (-----) [001] .... 16138.256881: mm_filemap_add_to_page_cache: dev 253:6 ino 758f page=000000001b375de1 pfn=755928 ofs=0
+ <...>-27215 (-----) [001] .... 16138.257526: mm_filemap_add_to_page_cache: dev 253:6 ino 7591 page=000000004e039481 pfn=841534 ofs=0
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.356491: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000d65af9d2 pfn=719246 ofs=161890304
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.357538: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000002f6ba74f pfn=864982 ofs=0
+ NonUserFacing6-5246 ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ <...>-27197 (-----) [005] .... 16140.143224: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=00000000a42527c6 pfn=1076669 ofs=32768
+ """
+
+ t2d = parse_trace_file_to_db(test_contents)
+ session = t2d.session
+
+ first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
+
+ #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
+
+ second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
+
+ # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
+
+def test_systrace_mm_filemap_add_to_pagecache():
+ test_contents = """
+<!DOCTYPE html>
+<html>
+<head i18n-values="dir:textdirection;">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta charset="utf-8"/>
+<title>Android System Trace</title>
+ <script class="trace-data" type="application/text">
+PROCESS DUMP
+USER PID PPID VSZ RSS WCHAN PC S NAME COMM
+root 1 0 62148 5976 0 0 S init [init]
+root 2 0 0 0 0 0 S [kthreadd] [kthreadd]
+ </script>
+
+ <script class="trace-data" type="application/text">
+MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+NonUserFacing6-5246 ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ </script>
+
+ <script class="trace-data" type="application/text">
+{"traceEvents": [{"category": "process_argv", "name": "process_argv", "args": {"argv": ["/mnt/ssd3/workspace/master/external/chromium-trace/systrace.py", "-t", "5", "pagecache"]}, "pid": 160383, "ts": 1037300940509.7991, "tid": 139628672526080, "ph": "M"}, {"category": "python", "name": "clock_sync", "args": {"issue_ts": 1037307346185.212, "sync_id": "9a7e4fe3-89ad-441f-8226-8fe533fe973e"}, "pid": 160383, "ts": 1037307351643.906, "tid": 139628726089536, "ph": "c"}], "metadata": {"clock-domain": "SYSTRACE"}}
+ </script>
+<!-- END TRACE -->
+ """
+
+ t2d = parse_trace_file_to_db(test_contents)
+ session = t2d.session
+
+ first_row = session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).first()
+
+ #dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x7580, page=0x0000000060e990c7, pfn=677646, ofs=159744), first_row)
+
+ second_to_last_row = session.query(MmFilemapAddToPageCache).filter(MmFilemapAddToPageCache.page.in_([0x000000006e0f8322])).first()
+
+ # dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row)
+
+def test_timestamp_filter():
+ test_contents = """
+ MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744
+ NonUserFacing6-5246 ( 1322) [005] .... 16139.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096
+ MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0
+ """
+
+ t2d = parse_trace_file_to_db(test_contents)
+ session = t2d.session
+
+ end_time = 16137.0
+
+ results = session.query(MmFilemapAddToPageCache).join(
+ MmFilemapAddToPageCache.raw_ftrace_entry).filter(
+ RawFtraceEntry.timestamp <= end_time).order_by(
+ MmFilemapAddToPageCache.id).all()
+
+ assert len(results) == 2
+ assert_eq_ignore_id(
+ MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0x7580, page=0x0000000060e990c7, pfn=677646,
+ ofs=159744), results[0])
+ assert_eq_ignore_id(
+ MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6,
+ ino=0xb1d8, page=0x0000000098d4d2e2, pfn=829676,
+ ofs=0), results[1])
+
+
+if __name__ == '__main__':
+ pytest.main()
diff --git a/startop/scripts/trace_analyzer/queries_all.sql b/startop/scripts/trace_analyzer/queries_all.sql
new file mode 100644
index 0000000..41d1c08
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_all.sql
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+-- filter for atrace writes
+CREATE VIEW IF NOT EXISTS tracing_mark_writes AS
+ SELECT *
+ FROM raw_ftrace_entries
+ WHERE function = 'tracing_mark_write';
+
+-- split the tracing_mark_write function args by ||s
+DROP TABLE IF exists tracing_mark_write_split_array;
+
+CREATE TABLE tracing_mark_write_split_array (
+ predictorset_id INT REFERENCES raw_ftrace_entries (id),
+ predictor_name,
+ rest,
+ gen,
+
+ UNIQUE(predictorset_id, gen) -- drops redundant inserts into table
+);
+
+CREATE INDEX "tracing_mark_write_split_array_id" ON tracing_mark_write_split_array (
+ predictorset_id COLLATE BINARY COLLATE BINARY
+);
+
+INSERT INTO tracing_mark_write_split_array
+ WITH
+ split(predictorset_id, predictor_name, rest, gen) AS (
+ -- split by |
+ SELECT id, '', function_args || '|', 0 FROM tracing_mark_writes WHERE id
+ UNION ALL
+ SELECT predictorset_id,
+ substr(rest, 0, instr(rest, '|')),
+ substr(rest, instr(rest, '|')+1),
+ gen + 1
+ FROM split
+ WHERE rest <> ''),
+ split_results AS (
+ SELECT * FROM split WHERE predictor_name <> ''
+ )
+ SELECT * from split_results
+;
+
+
diff --git a/startop/scripts/trace_analyzer/queries_app_launch_spans_with_name.sql b/startop/scripts/trace_analyzer/queries_app_launch_spans_with_name.sql
new file mode 100644
index 0000000..c28475e
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_app_launch_spans_with_name.sql
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+-- use the 'launching: $process_name' async slice to figure out launch duration.
+DROP VIEW IF EXISTS launch_durations_named;
+
+CREATE VIEW launch_durations_named AS
+WITH
+ launch_traces_raw AS (
+ SELECT *
+ FROM tracing_mark_write_split AS tmw,
+ raw_ftrace_entries AS rfe
+ WHERE atrace_message LIKE 'launching: %' AND rfe.id = tmw.raw_ftrace_entry_id
+ ),
+ launch_traces_joined AS (
+ SELECT started.timestamp AS started_timestamp,
+ finished.timestamp AS finished_timestamp,
+ started.id AS started_id,
+ finished.id AS finished_id,
+ SUBSTR(started.atrace_message, 12) AS proc_name -- crop out "launching: " from the string.
+ FROM launch_traces_raw AS started,
+ launch_traces_raw AS finished
+ -- async slices ('S' -> 'F') have matching counters given the same PID.
+ WHERE started.atrace_type == 'S'
+ AND finished.atrace_type == 'F'
+ AND started.atrace_count == finished.atrace_count
+ AND started.atrace_pid == finished.atrace_pid
+ )
+SELECT * from launch_traces_joined;
+
+SELECT * FROM launch_durations_named;
diff --git a/startop/scripts/trace_analyzer/queries_block_launch.sql b/startop/scripts/trace_analyzer/queries_block_launch.sql
new file mode 100644
index 0000000..34e5f03
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_block_launch.sql
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+DROP VIEW IF EXISTS blocked_iowait_for_app_launches;
+
+CREATE VIEW blocked_iowait_for_app_launches AS
+WITH
+ block_launch_join AS (
+ SELECT *
+ FROM blocking_durations AS bd,
+ launch_durations_named AS ld
+ WHERE bd.block_timestamp >= ld.started_timestamp
+ AND bd.unblock_timestamp <= ld.finished_timestamp
+ ),
+ blocked_ui_threads AS (
+ SELECT *
+ FROM start_process_ui_threads AS sp,
+ block_launch_join AS blj
+ WHERE sp.atm_ui_thread_tid == unblock_pid
+ AND sp.process_name = blj.proc_name
+ ),
+ summed_raw AS (
+ SELECT SUM(unblock_timestamp-block_timestamp)*1000 AS sum_block_duration_ms,
+ *
+ FROM blocked_ui_threads
+ GROUP BY unblock_pid
+ ),
+ summed_neat AS (
+ SELECT sum_block_duration_ms AS blocked_iowait_duration_ms,
+ process_name,
+ (finished_timestamp - started_timestamp) * 1000 AS launching_duration_ms,
+ started_timestamp * 1000 AS launching_started_timestamp_ms,
+ finished_timestamp * 1000 AS launching_finished_timestamp_ms
+ -- filter out the rest because its just selecting 1 arbitrary row (due to the SUM aggregate).,
+ FROM summed_raw
+ )
+SELECT * FROM summed_neat;
+
+SELECT * FROM blocked_iowait_for_app_launches;
diff --git a/startop/scripts/trace_analyzer/queries_find_sched_switch_unblocked.sql b/startop/scripts/trace_analyzer/queries_find_sched_switch_unblocked.sql
new file mode 100644
index 0000000..788d0da
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_find_sched_switch_unblocked.sql
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+DROP VIEW IF EXISTS sched_switch_iowaits_pre;
+
+-- scan for the closest pair such that:
+-- sched_block_reason pid=$PID iowait=1 ...
+-- ...
+-- sched_switch next_pid=$PID
+CREATE VIEW sched_switch_iowaits_pre AS
+ SELECT MAX(sbr.id) AS blocked_id,
+ ss.id AS sched_switch_id,
+ pid, -- iow.pid
+ iowait, -- iowait=0 or iowait=1
+ caller,
+ sbr_f.timestamp AS blocked_timestamp,
+ ss_f.timestamp AS sched_switch_timestamp,
+ next_comm, -- name of next_pid
+ next_pid -- same as iow.pid
+ FROM sched_blocked_reasons AS sbr,
+ raw_ftrace_entries AS sbr_f,
+ sched_switches AS ss,
+ raw_ftrace_entries AS ss_f
+ WHERE sbr_f.id == sbr.id
+ AND ss_f.id == ss.id
+ AND sbr.pid == ss.next_pid
+ AND sbr.iowait = 1
+ AND sbr_f.timestamp < ss_f.timestamp -- ensures the 'closest' sched_blocked_reason is selected.
+ GROUP BY ss.id
+;
+
+DROP VIEW IF EXISTS sched_switch_iowaits;
+
+CREATE VIEW sched_switch_iowaits AS
+ SELECT *, MIN(sched_switch_timestamp) AS ss_timestamp -- drop all of the 'too large' sched_switch entries except the closest one.
+ FROM sched_switch_iowaits_pre
+ GROUP BY blocked_id;
+
+SELECT * FROM sched_switch_iowaits;
+
+-- use a real table here instead of a view, otherwise SQLiteStudio segfaults for some reason.
+DROP TABLE IF EXISTS blocking_durations;
+
+CREATE TABLE blocking_durations AS
+WITH
+ blocking_durations_raw AS (
+ SELECT MAX(ss.id) AS block_id,
+ ssf.timestamp AS block_timestamp,
+ iow.sched_switch_timestamp AS unblock_timestamp,
+ ss.prev_comm as block_prev_comm,
+ iow.next_comm AS unblock_next_comm,
+ ss.prev_state AS block_prev_state,
+ iow.sched_switch_id AS unblock_id,
+ iow.pid AS unblock_pid,
+ iow.caller AS unblock_caller
+ FROM sched_switches AS ss, -- this is the sched_switch that caused a block (in the future when it unblocks, the reason is iowait=1).
+ sched_switch_iowaits AS iow, -- this is the sched_switch that removes the block (it is now running again).
+ raw_ftrace_entries AS ssf
+ WHERE ssf.id = ss.id AND ss.prev_pid == iow.next_pid AND ssf.timestamp < iow.sched_switch_timestamp
+ GROUP BY unblock_timestamp
+ ),
+ blocking_durations_tmp AS (
+ SELECT block_id,
+ unblock_timestamp,
+ block_timestamp,
+ block_prev_comm as comm,
+ block_prev_state as block_state,
+ unblock_id,
+ unblock_pid,
+ unblock_caller
+ FROM blocking_durations_raw
+ )
+ SELECT * FROM blocking_durations_tmp;-- ORDER BY block_id ASC;
+ --SELECT SUM(block_duration_ms) AS sum, * FROM blocking_durations GROUP BY unblock_pid ORDER BY sum DESC;
+
+DROP INDEX IF EXISTS "blocking_durations_block_timestamp";
+
+CREATE INDEX "blocking_durations_block_timestamp" ON blocking_durations (
+ block_timestamp COLLATE BINARY COLLATE BINARY
+);
+
+DROP INDEX IF EXISTS "blocking_durations_unblock_timestamp";
+
+CREATE INDEX "blocking_durations_unblock_timestamp" ON blocking_durations (
+ unblock_timestamp COLLATE BINARY COLLATE BINARY
+);
+
+SELECT * FROM blocking_durations;
diff --git a/startop/scripts/trace_analyzer/queries_get_comm_and_pids.sql b/startop/scripts/trace_analyzer/queries_get_comm_and_pids.sql
new file mode 100644
index 0000000..0c166b0
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_get_comm_and_pids.sql
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+DROP VIEW IF EXISTS sched_switch_next_comm_pids;
+
+CREATE VIEW IF NOT EXISTS sched_switch_next_comm_pids AS
+
+-- TODO: switch to using sched_switches table.
+
+WITH
+ sched_switchs AS (
+ SELECT * FROM raw_ftrace_entries WHERE function = 'sched_switch' AND function_args LIKE '% next_pid=%' AND function_args NOT LIKE '% next_comm=main %'
+ ),
+ comm_and_pids_raws AS (
+ SELECT id,
+ SUBSTR(function_args, instr(function_args, "next_comm="), instr(function_args, "next_pid=") - instr(function_args, "next_comm=")) AS next_comm_raw,
+ SUBSTR(function_args, instr(function_args, "next_pid="), instr(function_args, "next_prio=") - instr(function_args, "next_pid=")) AS next_pid_raw
+ FROM sched_switchs
+ ),
+ comm_and_pids AS (
+ SELECT id,
+ id AS raw_ftrace_entry_id,
+ TRIM(SUBSTR(next_pid_raw, 10)) AS next_pid, -- len("next_pid=") is 10
+ TRIM(SUBSTR(next_comm_raw, 11)) AS next_comm -- len("next_comm=") is 11
+ FROM comm_and_pids_raws
+ )
+SELECT * from comm_and_pids;
+
+SELECT * from sched_switch_next_comm_pids;
diff --git a/startop/scripts/trace_analyzer/queries_get_procs.sql b/startop/scripts/trace_analyzer/queries_get_procs.sql
new file mode 100644
index 0000000..06871c6
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_get_procs.sql
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+DROP VIEW IF EXISTS start_procs;
+
+CREATE VIEW IF NOT EXISTS start_procs AS
+WITH
+ start_procs_raw AS (
+ SELECT * from tracing_mark_write_split WHERE atrace_message LIKE 'Start proc: %'
+ ),
+ start_procs_substr AS (
+ -- note: "12" is len("Start proc: ")+1. sqlite indices start at 1.
+ SELECT raw_ftrace_entry_id, atrace_pid, SUBSTR(atrace_message, 13) AS process_name FROM start_procs_raw
+ )
+SELECT * from start_procs_substr;
+
+SELECT * from start_procs;
diff --git a/startop/scripts/trace_analyzer/queries_get_ui_threads.sql b/startop/scripts/trace_analyzer/queries_get_ui_threads.sql
new file mode 100644
index 0000000..876e50e
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_get_ui_threads.sql
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+-- note: These queries do comparisons based on raw_ftrace_entries.id by treating it as if it was equivalent to the temporal timestamp.
+-- in practice, the ID of raw_ftrace_entries is based on its order in the ftrace buffer [and on the same cpu its equivalent].
+-- we can always resort raw_ftrace_entries to ensure id order matches timestamp order. We should rarely need to compare by timestamp directly.
+-- accessing 'floats' is inferior as they are harder to index, and will result in slower queries.
+--
+-- Naming convention note: '_fid' corresponds to 'raw_ftrace_entry.id'.
+DROP VIEW IF EXISTS start_process_ui_threads;
+
+-- Map of started process names to their UI thread's TID (as returned by gettid).
+CREATE VIEW IF NOT EXISTS start_process_ui_threads AS
+WITH
+ start_proc_tids AS (
+ SELECT sp.raw_ftrace_entry_id AS start_proc_fid,
+ sp.atrace_pid AS atrace_pid,
+ sp.process_name AS process_name,
+ --MIN(nc.raw_ftrace_entry_id) as next_comm_fid,
+ nc.raw_ftrace_entry_id AS next_comm_fid,
+ nc.next_pid as next_pid,
+ nc.next_comm as next_comm,
+ SUBSTR(sp.process_name, -15) AS cut -- why -15? See TASK_MAX in kernel, the sched_switch name is truncated to 16 bytes.
+ FROM start_procs AS sp,
+ sched_switch_next_comm_pids AS nc
+ WHERE sp.process_name LIKE '%' || nc.next_comm -- kernel truncates the sched_switch::next_comm event, so we must match the prefix of the full name.
+ --WHERE SUBSTR(sp.process_name, -16) == nc.next_comm
+ --WHERE cut == nc.next_comm
+ ),
+ start_proc_tids_filtered AS (
+ SELECT *
+ FROM start_proc_tids
+ WHERE next_comm_fid > start_proc_fid -- safeguard that avoids choosing "earlier" sched_switch before process was even started.
+ --ORDER BY start_proc_fid, next_comm_fid
+ ),
+ start_proc_all_threads AS (
+ SELECT DISTINCT
+ start_proc_fid, -- this is the ftrace entry of the system server 'Start proc: $process_name'. only need this to join for timestamp.
+ process_name, -- this is the '$process_name' from the system server entry.
+ -- next up we have all the possible thread IDs as parsed from sched_switch that corresponds most closest to the start proc.
+ next_pid AS ui_thread_tpid, -- sched_switch.next_pid. This can be any of the threads in that process, it's not necessarily the main UI thread yet.
+ next_comm,
+ MIN(next_comm_fid) AS next_comm_fid -- don't pick the 'later' next_comm_fid because it could correspond to another app start.
+ FROM start_proc_tids_filtered
+ GROUP BY start_proc_fid, ui_thread_tpid
+ ),
+ activity_thread_mains AS (
+ SELECT * FROM tracing_mark_write_split WHERE atrace_message = 'ActivityThreadMain'
+ ),
+ start_proc_ui_threads AS (
+ SELECT start_proc_fid,
+ process_name,
+ ui_thread_tpid,
+ next_comm,
+ next_comm_fid,
+ atm.raw_ftrace_entry_id as atm_fid,
+ atm.atrace_pid as atm_ui_thread_tid
+ FROM start_proc_all_threads AS spt,
+ activity_thread_mains AS atm
+ WHERE atm.atrace_pid == spt.ui_thread_tpid AND atm.raw_ftrace_entry_id > spt.start_proc_fid -- Ensure we ignore earlier ActivityThreadMains prior to their Start proc.
+ ),
+ start_proc_ui_threads_filtered AS (
+ SELECT start_proc_fid,
+ process_name, -- e.g. 'com.android.settings'
+ --ui_thread_tpid,
+ --next_comm,
+ --next_comm_fid,
+ MIN(atm_fid) AS atm_fid,
+ atm_ui_thread_tid -- equivalent to gettid() for the process's UI thread.
+ FROM start_proc_ui_threads
+ GROUP BY start_proc_fid, atm_ui_thread_tid -- find the temporally closest ActivityTaskMain to a "Start proc: $process_name"
+ )
+SELECT * FROM start_proc_ui_threads_filtered;
+
+SELECT * FROM start_process_ui_threads;
diff --git a/startop/scripts/trace_analyzer/queries_mark_write_join.sql b/startop/scripts/trace_analyzer/queries_mark_write_join.sql
new file mode 100644
index 0000000..100f0740
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_mark_write_join.sql
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+DROP TABLE IF EXISTS tracing_mark_write_split;
+
+CREATE TABLE tracing_mark_write_split (
+ raw_ftrace_entry_id INT REFERENCES raw_ftrace_entries (id),
+ atrace_type CHAR(1), -- only null for the first 2 sync timers. usually 'B', 'C', E', ...
+ atrace_pid INT, -- only null for first 2 sync timers
+ atrace_message, -- usually null for type='E' etc.
+ atrace_count, -- usually non-null only for 'C'
+
+ UNIQUE(raw_ftrace_entry_id) -- drops redundant inserts into table
+);
+
+INSERT INTO tracing_mark_write_split
+WITH
+ pivoted AS (
+ SELECT tx.predictorset_id,
+ --ty.predictorset_id,
+ --tz.predictorset_id,
+ --tzz.predictorset_id,
+ tx.predictor_name AS atrace_type,
+ CAST(ty.predictor_name AS integer) AS atrace_pid,
+ tz.predictor_name AS atrace_message,
+ CAST(tzz.predictor_name AS integer) AS atrace_count
+ FROM (SELECT * from tracing_mark_write_split_array WHERE gen = 1) AS tx
+ LEFT JOIN
+ (SELECT * FROM tracing_mark_write_split_array WHERE gen = 2) AS ty
+ ON tx.predictorset_id = ty.predictorset_id
+ LEFT JOIN
+ (SELECT * FROM tracing_mark_write_split_array WHERE gen = 3) AS tz
+ ON tx.predictorset_id = tz.predictorset_id
+ LEFT JOIN
+ (SELECT * FROM tracing_mark_write_split_array WHERE gen = 4) AS tzz
+ ON tx.predictorset_id = tzz.predictorset_id
+ )
+SELECT * from pivoted ORDER BY predictorset_id;-- LIMIT 100;
+
+SELECT * FROM tracing_mark_write_split;
diff --git a/startop/scripts/trace_analyzer/queries_pretty_print_block_launch.sql b/startop/scripts/trace_analyzer/queries_pretty_print_block_launch.sql
new file mode 100644
index 0000000..bf5e3cc
--- /dev/null
+++ b/startop/scripts/trace_analyzer/queries_pretty_print_block_launch.sql
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+.headers on
+.mode quote
+
+SELECT * FROM blocked_iowait_for_app_launches;
+
+/*
+Output as CSV example:
+
+'blocked_iowait_duration_ms','process_name','launching_duration_ms','launching_started_timestamp_ms','launching_finished_timestamp_ms'
+125.33199995596078224,'com.android.settings',1022.4840000009862706,17149896.822000000626,17150919.305999998003
+
+*/
diff --git a/startop/scripts/trace_analyzer/run-sql-queries b/startop/scripts/trace_analyzer/run-sql-queries
new file mode 100755
index 0000000..61a0ad4
--- /dev/null
+++ b/startop/scripts/trace_analyzer/run-sql-queries
@@ -0,0 +1,79 @@
+#!/bin/bash
+# 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.
+
+DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+
+if [[ $# -lt 1 ]]; then
+ echo "Usage: $0 <db-file>"
+fi
+
+DB_TARGET=$1
+
+if ! [[ -f $DB_TARGET ]]; then
+ echo "ERROR: File '$DB_TARGET' does not exist." >&2
+ exit 1
+fi
+
+exec_sql_file() {
+ local filename="$1"
+ if ! [[ -f $filename ]]; then
+ echo "ERROR: Can't exec SQL file, '$filename' does not exist." >&2
+ return 1
+ fi
+
+ sqlite3 "$DB_TARGET" < "$DIR"/"$filename"
+}
+
+exec_sql_file_quiet() {
+ exec_sql_file "$@" > /dev/null
+}
+
+# Some views/tables need other views already created, so order does matter.
+# x -> y , means x depends on y.
+
+# View: tracing_mark_writes
+# Table: tracing_mark_write_split_array -> tracing_mark_writes
+exec_sql_file_quiet "queries_all.sql"
+
+# Table: tracing_mark_write_split -> tracing_mark_write_split_array
+exec_sql_file_quiet "queries_mark_write_join.sql"
+
+# View: start_procs -> tracing_mark_write_split
+exec_sql_file_quiet "queries_get_procs.sql"
+
+# View: sched_switch_next_comm_pids
+exec_sql_file_quiet "queries_get_comm_and_pids.sql"
+
+# View: start_process_ui_threads -> start_procs, sched_switch_next_comm_pids
+exec_sql_file_quiet "queries_get_ui_threads.sql"
+
+# View: launch_durations_named -> tracing_mark_write_split
+exec_sql_file_quiet "queries_app_launch_spans_with_name.sql"
+
+# View: sched_switch_iowaits_pre
+# View: sched_switch_iowaits -> sched_switch_iowaits_pre
+# Table: blocking_durations -> sched_switch_iowaits
+exec_sql_file_quiet "queries_find_sched_switch_unblocked.sql"
+
+# View: blocked_iowait_for_app_launches -> launch_durations_named, blocking_durations
+exec_sql_file_quiet "queries_block_launch.sql"
+
+#####
+#####
+#####
+
+# Final queries
+
+exec_sql_file "queries_pretty_print_block_launch.sql"
diff --git a/startop/scripts/trace_analyzer/test_fixtures/common_systrace b/startop/scripts/trace_analyzer/test_fixtures/common_systrace
new file mode 100644
index 0000000..802cb55
--- /dev/null
+++ b/startop/scripts/trace_analyzer/test_fixtures/common_systrace
@@ -0,0 +1,518 @@
+# tracer: nop
+#
+# entries-in-buffer/entries-written: 411983/411983 #P:8
+#
+# _-----=> irqs-off
+# / _----=> need-resched
+# | / _---=> hardirq/softirq
+# || / _--=> preempt-depth
+# ||| / delay
+# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION
+# | | | | |||| | |
+ <...>-14603 (-----) [000] ...1 14592.893157: tracing_mark_write: trace_event_clock_sync: parent_ts=14592.892578
+ <...>-14603 (-----) [000] ...1 14592.893172: tracing_mark_write: trace_event_clock_sync: realtime_ts=1557129597951
+ <...>-18150 (-----) [004] d..2 14594.182110: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=kworker/u16:16 next_pid=23269 next_prio=120
+ kworker/u16:16-23269 (23269) [004] d.h3 14594.182228: sched_blocked_reason: pid=18150 iowait=0 caller=a6xx_oob_set+0x194/0x3dc
+ kworker/u16:16-23269 (23269) [004] d..2 14594.182248: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=D ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.182312: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.182488: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ kworker/u16:16-23269 (23269) [005] d..2 14594.182610: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.182626: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.182755: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.182975: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.183209: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.183371: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.184286: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ kworker/u16:16-23269 (23269) [005] d..2 14594.184495: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
+ <...>-18150 (-----) [004] d..2 14594.184498: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ ksoftirqd/4-47 ( 47) [004] d..2 14594.185678: sched_switch: prev_comm=ksoftirqd/4 prev_pid=47 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120
+ kworker/6:2-10610 (10610) [006] d..2 14594.186012: sched_switch: prev_comm=kworker/6:2 prev_pid=10610 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-656 (-----) [001] .... 14594.219464: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-1803 (-----) [000] d..2 14594.219595: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-3359 (-----) [001] ...1 14594.219856: tracing_mark_write: S|1368|launching: com.google.android.dialer|0
+ <...>-3359 (-----) [001] ...1 14594.219863: tracing_mark_write: B|1368|MetricsLogger:launchObserverNotifyActivityLaunched
+ <...>-3359 (-----) [001] ...1 14594.219869: tracing_mark_write: B|1368|MetricsLogger:convertActivityRecordToProto
+ <...>-1398 (-----) [006] ...1 14594.220160: tracing_mark_write: B|1368|updateInputWindows
+ <...>-3359 (-----) [001] .... 14594.220230: binder_set_priority: proc=1368 thread=3359 old=110 => new=120 desired=120
+ <...>-1398 (-----) [006] ...1 14594.220588: tracing_mark_write: B|1368|android.os.Handler: com.android.server.wm.AppWindowToken$1
+ <...>-1398 (-----) [006] ...1 14594.220722: tracing_mark_write: B|1368|ResourcesManager#getResources
+ <...>-1052 (-----) [002] d..2 14594.220884: sched_switch: prev_comm=statsd.writer prev_pid=1052 prev_prio=120 prev_state=S ==> next_comm=UiThreadHelper next_pid=2045 next_prio=118
+ <...>-1398 (-----) [006] ...1 14594.220926: tracing_mark_write: B|1368|Theme::ApplyStyle
+ <...>-1398 (-----) [006] ...1 14594.220929: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-2007 (-----) [007] ...1 14594.220996: tracing_mark_write: B|2007|Choreographer#doFrame
+ <...>-2007 (-----) [007] ...1 14594.221005: tracing_mark_write: B|2007|animation
+ <...>-1398 (-----) [006] ...1 14594.221015: tracing_mark_write: B|1368|ResourcesManager#getResources
+ <...>-2045 (-----) [002] ...2 14594.221035: binder_set_priority: proc=1368 thread=1903 old=120 => new=118 desired=118
+ <...>-2045 (-----) [002] d..2 14594.221065: sched_switch: prev_comm=UiThreadHelper prev_pid=2045 prev_prio=118 prev_state=S ==> next_comm=Binder:1368_4 next_pid=1903 next_prio=118
+ <...>-1398 (-----) [006] ...1 14594.221080: tracing_mark_write: B|1368|AssetManager::SetApkAssets
+ <...>-2007 (-----) [007] ...1 14594.221110: tracing_mark_write: B|2007|traversal
+ <...>-656 (-----) [000] ...1 14594.221137: tracing_mark_write: B|625|requestNextVsync
+ <...>-656 (-----) [000] ...1 14594.221141: tracing_mark_write: B|625|resetIdleTimer
+ <...>-2007 (-----) [007] ...1 14594.221146: tracing_mark_write: B|2007|draw
+ <...>-2007 (-----) [007] ...1 14594.221160: tracing_mark_write: B|2007|Record View#draw()
+ <...>-660 (-----) [005] d..2 14594.221285: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ <...>-658 (-----) [004] d..2 14594.221327: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=android.display next_pid=1397 next_prio=117
+ <...>-2738 (-----) [005] ...1 14594.221342: tracing_mark_write: B|2007|notifyFramePending
+ <...>-2738 (-----) [005] ...1 14594.221362: tracing_mark_write: B|2007|DrawFrame
+ <...>-2738 (-----) [005] ...1 14594.221369: tracing_mark_write: B|2007|query
+ <...>-2007 (-----) [007] d..2 14594.221369: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-1903 (-----) [002] .... 14594.221397: binder_set_priority: proc=1368 thread=1903 old=118 => new=120 desired=120
+ <...>-2738 (-----) [005] ...2 14594.221400: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-2738 (-----) [005] d..2 14594.221430: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-1368 (-----) [003] ...1 14594.221431: tracing_mark_write: B|1368|Lock contention on GC thread flip lock (owner tid: 0)
+ <...>-656 (-----) [005] ...1 14594.221460: tracing_mark_write: B|625|query
+ <...>-656 (-----) [005] .... 14594.221528: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-2738 (-----) [007] ...1 14594.221552: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...2 14594.221563: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-2738 (-----) [007] d..2 14594.221600: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-1368 (-----) [003] d..2 14594.221623: sched_switch: prev_comm=system_server prev_pid=1368 prev_prio=118 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120
+ <...>-656 (-----) [007] ...1 14594.221628: tracing_mark_write: B|625|query
+ <...>-23031 (-----) [001] d..2 14594.221643: sched_switch: prev_comm=UiAutomation prev_pid=23031 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-2738 (-----) [007] ...1 14594.221664: tracing_mark_write: B|2007|syncFrameState
+ <...>-2738 (-----) [007] ...1 14594.221697: tracing_mark_write: B|2007|prepareTree
+ <...>-23008 (-----) [005] d..2 14594.221706: sched_switch: prev_comm=hub.uiautomator prev_pid=23008 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
+ <...>-656 (-----) [000] .... 14594.221737: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-1803 (-----) [003] d..2 14594.221747: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120
+ <...>-1397 (-----) [004] d..2 14594.221806: sched_switch: prev_comm=android.display prev_pid=1397 prev_prio=117 prev_state=S ==> next_comm=Binder:2007_A next_pid=4180 next_prio=120
+ <...>-1398 (-----) [006] d..2 14594.221816: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=110 prev_state=R ==> next_comm=s.nexuslauncher next_pid=2007 next_prio=110
+ <...>-2738 (-----) [007] ...1 14594.221824: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221830: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221834: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221841: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221843: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221846: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.221850: tracing_mark_write: B|2007|dequeueBuffer
+ <...>-2738 (-----) [007] ...2 14594.221864: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-2738 (-----) [007] d..2 14594.221985: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=crtc_event:97 next_pid=303 next_prio=83
+ <...>-2007 (-----) [006] ...1 14594.221989: tracing_mark_write: B|2007|topResumedActivityChangeItem
+ <...>-303 (-----) [007] d..2 14594.222016: sched_switch: prev_comm=crtc_event:97 prev_pid=303 prev_prio=83 prev_state=S ==> next_comm=rcu_preempt next_pid=7 next_prio=120
+ rcu_preempt-7 ( 7) [007] d..2 14594.222035: sched_switch: prev_comm=rcu_preempt prev_pid=7 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ migration/4-46 ( 46) [004] d..2 14594.222037: sched_switch: prev_comm=migration/4 prev_pid=46 prev_prio=0 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-2738 (-----) [007] d..2 14594.222039: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120
+ <...>-656 (-----) [004] ...1 14594.222100: tracing_mark_write: B|625|dequeueBuffer
+ <...>-656 (-----) [004] ...1 14594.222114: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2
+ <...>-2007 (-----) [006] ...2 14594.222131: binder_set_priority: proc=1368 thread=1903 old=120 => new=110 desired=110
+ <...>-2007 (-----) [006] d..2 14594.222143: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=UiThreadHelper next_pid=2045 next_prio=118
+ <...>-2613 (-----) [001] d..2 14594.222158: sched_switch: prev_comm=ogle.android.as prev_pid=2613 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-18150 (-----) [007] d..2 14594.222193: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-656 (-----) [004] .... 14594.222220: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-2738 (-----) [007] ...1 14594.222267: tracing_mark_write: B|2007|HWC release fence 36027 has signaled
+ <...>-656 (-----) [007] ...1 14594.223842: tracing_mark_write: B|625|queueBuffer
+ <...>-656 (-----) [007] ...1 14594.223845: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2
+ <...>-656 (-----) [007] ...1 14594.223871: tracing_mark_write: B|625|requestNextVsync
+ <...>-656 (-----) [007] ...1 14594.223873: tracing_mark_write: B|625|resetIdleTimer
+ <...>-656 (-----) [007] ...1 14594.223881: tracing_mark_write: B|625|addAndGetFrameTimestamps
+ <...>-1395 (-----) [001] d..2 14594.223909: sched_switch: prev_comm=android.ui prev_pid=1395 prev_prio=118 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-2738 (-----) [007] ...1 14594.223959: tracing_mark_write: B|2007|Trace GPU completion fence 36027
+ <...>-11799 (-----) [006] ...1 14594.224006: tracing_mark_write: B|2007|waiting for GPU completion 36027
+ <...>-11799 (-----) [006] ...1 14594.224009: tracing_mark_write: B|2007|waitForever
+ <...>-2613 (-----) [004] d..2 14594.224014: sched_switch: prev_comm=ogle.android.as prev_pid=2613 prev_prio=120 prev_state=S ==> next_comm=Binder:1803_6 next_pid=2173 next_prio=120
+ <...>-11799 (-----) [006] d..1 14594.224014: fence_enable_signal: driver=kgsl-timeline timeline=kgsl-3d0_13-s.nexuslauncher(200 context=27 seqno=78002
+ <...>-11799 (-----) [006] d..2 14594.224021: sched_switch: prev_comm=GPU completion prev_pid=11799 prev_prio=110 prev_state=S ==> next_comm=rcuop/6 next_pid=68 next_prio=120
+ rcuop/6-68 ( 68) [006] d..2 14594.224044: sched_switch: prev_comm=rcuop/6 prev_pid=68 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-259 (-----) [006] d..2 14594.224132: sched_switch: prev_comm=kgsl_worker_thr prev_pid=259 prev_prio=97 prev_state=S ==> next_comm=Binder:2007_A next_pid=4180 next_prio=120
+ <...>-3206 (-----) [001] d..2 14594.224167: sched_switch: prev_comm=aiai-vc-0 prev_pid=3206 prev_prio=139 prev_state=R ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120
+ lowpool[847]-14589 ( 2446) [005] d..1 14594.224300: mm_filemap_delete_from_page_cache: dev 0:1 ino 3d0034 page=000000008247d586 pfn=676904 ofs=0
+ <...>-1803 (-----) [001] d..2 14594.224302: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=aiai-vc-0 next_pid=3206 next_prio=139
+ <...>-3206 (-----) [001] d..2 14594.224433: sched_switch: prev_comm=aiai-vc-0 prev_pid=3206 prev_prio=139 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-1903 (-----) [003] ...1 14594.224490: tracing_mark_write: B|1368|dispatchingStartProcess:com.google.android.dialer
+ <...>-1903 (-----) [003] ...1 14594.224659: tracing_mark_write: B|1368|wmLayout
+ <...>-1903 (-----) [003] ...1 14594.224666: tracing_mark_write: B|1368|performSurfacePlacement
+ <...>-1903 (-----) [003] ...1 14594.224683: tracing_mark_write: B|1368|applySurfaceChanges
+ <...>-1903 (-----) [003] ...1 14594.224688: tracing_mark_write: B|1368|openSurfaceTransaction
+ <...>-2738 (-----) [007] ...1 14594.224711: tracing_mark_write: B|2007|query
+ <...>-1903 (-----) [003] ...1 14594.224714: tracing_mark_write: B|1368|performLayout
+ <...>-2738 (-----) [007] ...1 14594.224714: tracing_mark_write: B|2007|query
+ <...>-1903 (-----) [003] ...1 14594.224723: tracing_mark_write: B|1368|applyPostLayoutPolicy
+ <...>-2738 (-----) [007] d..2 14594.224752: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-656 (-----) [007] .... 14594.224766: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-1398 (-----) [002] ...1 14594.224801: tracing_mark_write: B|1368|Theme::ApplyStyle
+ <...>-1398 (-----) [002] ...1 14594.224805: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224820: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224826: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224833: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224838: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224846: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224853: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224859: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-1398 (-----) [002] ...1 14594.224864: tracing_mark_write: B|1368|AssetManager::GetBag
+ <...>-18150 (-----) [006] d..2 14594.228407: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98
+ <...>-2738 (-----) [007] d..2 14594.228411: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=kworker/7:0H next_pid=76 next_prio=100
+ <...>-1409 (-----) [004] ...1 14594.228417: tracing_mark_write: B|1368|Start proc: com.google.android.dialer
+ <...>-440 (-----) [006] d..2 14594.228418: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120
+ <...>-76 (-----) [007] d..2 14594.228430: sched_switch: prev_comm=kworker/7:0H prev_pid=76 prev_prio=100 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98
+ <...>-440 (-----) [007] d..2 14594.228434: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/7:0H next_pid=76 next_prio=100
+ <...>-18150 (-----) [006] d..3 14594.228442: sched_blocked_reason: pid=1398 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f8
+ <...>-76 (-----) [007] d..2 14594.228442: sched_switch: prev_comm=kworker/7:0H prev_pid=76 prev_prio=100 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ <...>-2738 (-----) [007] ...2 14594.228446: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-18150 (-----) [006] d..2 14594.228447: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=110
+ <...>-2738 (-----) [007] d..2 14594.228479: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-1409 (-----) [004] d..2 14594.228499: sched_switch: prev_comm=ActivityManager prev_pid=1409 prev_prio=118 prev_state=D ==> next_comm=Binder:965_2 next_pid=1041 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229271: tracing_mark_write: B|625|handleTransaction
+ <...>-1773 (-----) [004] .... 14594.229285: binder_set_priority: proc=625 thread=1773 old=110 => new=120 desired=120
+ <...>-440 (-----) [007] d..2 14594.229301: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ <...>-2738 (-----) [007] ...1 14594.229318: tracing_mark_write: B|2007|HWC release fence 36028 has signaled
+ <...>-2738 (-----) [007] ...1 14594.229331: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.229337: tracing_mark_write: B|2007|eglBeginFrame
+ <...>-2738 (-----) [007] ...1 14594.229352: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...1 14594.229354: tracing_mark_write: B|2007|query
+ <...>-791 (-----) [000] d..2 14594.229357: sched_switch: prev_comm=main prev_pid=791 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229440: tracing_mark_write: B|625|doTransaction
+ <...>-13916 (-----) [002] d..2 14594.229482: sched_switch: prev_comm=HeapTaskDaemon prev_pid=13916 prev_prio=124 prev_state=D|K ==> next_comm=swapper/2 next_pid=0 next_prio=120
+ <...>-13917 (-----) [001] d..2 14594.229492: sched_blocked_reason: pid=13916 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-625 (-----) [003] ...1 14594.229492: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229507: tracing_mark_write: B|625|doTransaction
+ <...>-13917 (-----) [001] d..2 14594.229523: sched_switch: prev_comm=ReferenceQueueD prev_pid=13917 prev_prio=124 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-13916 (-----) [002] d..2 14594.229535: sched_blocked_reason: pid=13917 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-625 (-----) [003] ...1 14594.229538: tracing_mark_write: B|625|doTransaction
+ <...>-2738 (-----) [007] ...1 14594.229543: tracing_mark_write: B|2007|flush commands
+ <...>-13916 (-----) [002] .... 14594.229562: sched_process_exit: comm=HeapTaskDaemon pid=13916 prio=124
+ <...>-625 (-----) [003] ...1 14594.229567: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229588: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229628: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229652: tracing_mark_write: B|625|doTransaction
+ <...>-13916 (-----) [002] d..2 14594.229676: sched_switch: prev_comm=HeapTaskDaemon prev_pid=13916 prev_prio=124 prev_state=x ==> next_comm=swapper/2 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229676: tracing_mark_write: B|625|doTransaction
+ <...>-2007 (-----) [006] d..2 14594.229688: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229703: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229725: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229750: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229772: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.229792: tracing_mark_write: B|625|doTransaction
+ <...>-791 (-----) [000] d..2 14594.229811: sched_switch: prev_comm=main prev_pid=791 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229824: tracing_mark_write: B|625|doTransaction
+ <...>-2738 (-----) [007] ...1 14594.229827: tracing_mark_write: B|2007|eglSwapBuffersWithDamageKHR
+ <...>-13917 (-----) [001] d..2 14594.229836: sched_switch: prev_comm=ReferenceQueueD prev_pid=13917 prev_prio=124 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-2738 (-----) [007] ...1 14594.229837: tracing_mark_write: B|2007|setSurfaceDamage
+ <...>-625 (-----) [003] ...1 14594.229850: tracing_mark_write: B|625|doTransaction
+ <...>-13918 (-----) [002] d..2 14594.229856: sched_blocked_reason: pid=13917 iowait=0 caller=SyS_madvise+0xd34/0xd3c
+ <...>-5281 (-----) [001] d..2 14594.229932: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-89 (-----) [006] d..2 14594.229951: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.229982: tracing_mark_write: B|625|handleMessageInvalidate
+ <...>-625 (-----) [003] ...1 14594.229984: tracing_mark_write: B|625|handlePageFlip
+ <...>-625 (-----) [003] ...1 14594.230013: tracing_mark_write: B|625|latchBuffer
+ <...>-13917 (-----) [000] .... 14594.230015: sched_process_exit: comm=ReferenceQueueD pid=13917 prio=124
+ <...>-625 (-----) [003] ...1 14594.230020: tracing_mark_write: B|625|query
+ <...>-625 (-----) [003] ...1 14594.230028: tracing_mark_write: B|625|updateTexImage
+ <...>-625 (-----) [003] ...1 14594.230035: tracing_mark_write: B|625|acquireBuffer
+ <...>-625 (-----) [003] ...1 14594.230044: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2
+ <...>-2738 (-----) [007] d..2 14594.230057: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=D ==> next_comm=smem_native_lpa next_pid=88 next_prio=120
+ <...>-14607 (-----) [000] d..2 14594.259609: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-2738 (-----) [005] d..2 14594.259620: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120
+ <...>-1773 (-----) [005] ...1 14594.259649: tracing_mark_write: B|625|query
+ <...>-2738 (-----) [005] ...1 14594.259714: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] d..2 14594.259743: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120
+ <...>-1773 (-----) [005] ...1 14594.259757: tracing_mark_write: B|625|query
+ <...>-2738 (-----) [005] ...1 14594.259810: tracing_mark_write: B|2007|syncFrameState
+ <...>-2738 (-----) [005] ...1 14594.259856: tracing_mark_write: B|2007|prepareTree
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259863: tracing_mark_write: B|14607|AttachCurrentThread
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259869: tracing_mark_write: B|14607|Thread::Attach
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259873: tracing_mark_write: B|14607|Thread birth
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259916: tracing_mark_write: B|14607|Thread::Init
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259920: tracing_mark_write: B|14607|InitStackHwm
+ <...>-14607 (-----) [000] d..2 14594.259932: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_1-14624 (14607) [002] d..2 14594.259941: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-3198 (-----) [001] ...1 14594.259942: tracing_mark_write: B|2007|Update SurfaceView position
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259963: tracing_mark_write: B|14607|InitTlsEntryPoints
+ Binder:14607_1-14624 (14607) [002] ...1 14594.259974: tracing_mark_write: B|14607|InitInterpreterTls
+ <...>-14607 (-----) [000] d..2 14594.260005: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-3198 (-----) [001] d..2 14594.260007: sched_switch: prev_comm=hwuiTask1 prev_pid=3198 prev_prio=118 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-14607 (-----) [000] d..2 14594.260024: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_1-14624 (14607) [002] d..2 14594.260038: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260064: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ Binder:14607_1-14624 (14607) [002] ...1 14594.260101: tracing_mark_write: B|14607|ThreadList::Register
+ <...>-2738 (-----) [005] ...1 14594.260128: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260140: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260148: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260155: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260161: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260167: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260173: tracing_mark_write: B|2007|dequeueBuffer
+ <...>-2007 (-----) [001] d..2 14594.260201: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-2738 (-----) [005] d..2 14594.260214: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120
+ <...>-1773 (-----) [005] ...1 14594.260236: tracing_mark_write: B|625|dequeueBuffer
+ <...>-1773 (-----) [005] ...1 14594.260249: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2
+ <...>-14607 (-----) [000] d..2 14594.260334: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_1-14624 (14607) [002] d..2 14594.260343: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260376: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] d..2 14594.260387: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-2738 (-----) [005] ...1 14594.260401: tracing_mark_write: B|2007|HWC release fence 36030 has signaled
+ Binder:14607_1-14624 (14607) [002] d..2 14594.260407: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-2738 (-----) [005] ...1 14594.260419: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260427: tracing_mark_write: B|2007|eglBeginFrame
+ <...>-2738 (-----) [005] ...1 14594.260445: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [005] ...1 14594.260450: tracing_mark_write: B|2007|query
+ Binder:14607_1-14624 (14607) [002] .... 14594.260472: task_newtask: pid=14625 comm=Binder:14607_1 clone_flags=3d0f00 oom_score_adj=-1000
+ <...>-14607 (-----) [000] d..2 14594.260517: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260525: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260555: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] ...1 14594.260569: tracing_mark_write: B|14607|ActivityThreadMain
+ <...>-14607 (-----) [000] d..2 14594.260581: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260588: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260611: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] d..2 14594.260623: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260636: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260663: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] d..2 14594.260674: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260694: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.260724: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-2738 (-----) [005] ...1 14594.260734: tracing_mark_write: B|2007|flush commands
+ <...>-14607 (-----) [000] d..2 14594.260735: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260753: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ Binder:14607_2-14625 (14607) [001] ...1 14594.260925: tracing_mark_write: B|14607|AttachCurrentThread
+ Binder:14607_2-14625 (14607) [001] ...1 14594.260930: tracing_mark_write: B|14607|Thread::Attach
+ Binder:14607_2-14625 (14607) [001] ...1 14594.260933: tracing_mark_write: B|14607|Thread birth
+ Binder:14607_2-14625 (14607) [001] ...1 14594.260973: tracing_mark_write: B|14607|Thread::Init
+ Binder:14607_2-14625 (14607) [001] ...1 14594.260977: tracing_mark_write: B|14607|InitStackHwm
+ <...>-14607 (-----) [000] d..2 14594.260990: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.260998: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ Binder:14607_2-14625 (14607) [001] ...1 14594.261023: tracing_mark_write: B|14607|InitTlsEntryPoints
+ Binder:14607_2-14625 (14607) [001] ...1 14594.261034: tracing_mark_write: B|14607|InitInterpreterTls
+ <...>-14607 (-----) [000] d..2 14594.261064: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] d..2 14594.261075: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.261094: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] d..2 14594.261120: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-14607 (-----) [000] d..2 14594.261132: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ Binder:14607_2-14625 (14607) [001] d..2 14594.261146: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ Binder:14607_2-14625 (14607) [001] ...1 14594.261167: tracing_mark_write: B|14607|ThreadList::Register
+ <...>-14607 (-----) [000] d..2 14594.261209: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754
+ <...>-2738 (-----) [005] ...1 14594.261212: tracing_mark_write: B|2007|waitOnFences
+ <...>-14607 (-----) [000] d..2 14594.261220: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-2738 (-----) [005] ...1 14594.261232: tracing_mark_write: B|2007|eglSwapBuffersWithDamageKHR
+ <...>-2738 (-----) [005] ...1 14594.261244: tracing_mark_write: B|2007|setSurfaceDamage
+ Binder:14607_2-14625 (14607) [001] d..2 14594.261246: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc
+ <...>-14607 (-----) [000] ...1 14594.261326: tracing_mark_write: B|14607|VerifyClass com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder
+ <...>-2738 (-----) [005] .... 14594.261621: fence_init: driver=kgsl-timeline timeline=kgsl-3d0_13-s.nexuslauncher(200 context=27 seqno=78005
+ <...>-625 (-----) [003] ...1 14594.263903: tracing_mark_write: B|625|resetIdleTimer
+ <...>-625 (-----) [003] ...1 14594.263912: tracing_mark_write: B|625|rebuildLayerStacks
+ <...>-625 (-----) [003] ...1 14594.263915: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty
+ <...>-625 (-----) [003] ...1 14594.263919: tracing_mark_write: B|625|computeVisibleRegions
+ <...>-1398 (-----) [006] d..2 14594.263966: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=110 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120
+ <...>-1695 (-----) [001] d..2 14594.264086: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=Binder:1368_14 next_pid=3253 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.264293: tracing_mark_write: B|625|calculateWorkingSet
+ <...>-625 (-----) [003] ...1 14594.264500: tracing_mark_write: B|625|prepare
+ <...>-625 (-----) [003] ...1 14594.264513: tracing_mark_write: B|625|HIDL::IComposerClient::executeCommands_2_2::client
+ <...>-625 (-----) [003] ...2 14594.264584: binder_set_priority: proc=627 thread=627 old=97 => new=98 desired=98
+ <...>-625 (-----) [003] d..2 14594.264617: sched_switch: prev_comm=surfaceflinger prev_pid=625 prev_prio=98 prev_state=S ==> next_comm=logd.writer next_pid=588 next_prio=130
+ <...>-588 (-----) [003] d..2 14594.264851: sched_switch: prev_comm=logd.writer prev_pid=588 prev_prio=130 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120
+ rcu_preempt-7 ( 7) [007] d..2 14594.265273: sched_switch: prev_comm=rcu_preempt prev_pid=7 prev_prio=120 prev_state=S ==> next_comm=kworker/u16:3 next_pid=18008 next_prio=120
+ <...>-18008 (-----) [007] d..2 14594.265404: sched_switch: prev_comm=kworker/u16:3 prev_pid=18008 prev_prio=120 prev_state=D ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-18008 (-----) [007] d..2 14594.265471: sched_switch: prev_comm=kworker/u16:3 prev_pid=18008 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.265496: tracing_mark_write: B|625|doComposition
+ <...>-625 (-----) [003] ...1 14594.265507: tracing_mark_write: B|625|doComposeSurfaces
+ <...>-625 (-----) [003] ...1 14594.265552: tracing_mark_write: B|625|acquireBuffer
+ <...>-625 (-----) [003] ...1 14594.265563: tracing_mark_write: B|625|postFramebuffer
+ <...>-625 (-----) [003] ...1 14594.265567: tracing_mark_write: B|625|presentAndGetReleaseFences
+ <...>-625 (-----) [003] d..1 14594.265601: fence_enable_signal: driver=sde_fence:crtc97:91650 timeline=crtc97 context=3 seqno=91650
+ <...>-625 (-----) [003] ...1 14594.265735: tracing_mark_write: B|625|logLayerStats
+ <...>-625 (-----) [003] ...1 14594.265744: tracing_mark_write: B|625|postComposition
+ <...>-625 (-----) [003] ...1 14594.265749: tracing_mark_write: B|625|releaseBuffer
+ <...>-625 (-----) [003] ...1 14594.265753: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 1
+ <...>-625 (-----) [003] ...1 14594.265791: tracing_mark_write: B|625|releaseBuffer
+ <...>-440 (-----) [007] d..2 14594.342366: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/u17:2 next_pid=1778 next_prio=100
+ <...>-2007 (-----) [006] ...1 14594.342375: tracing_mark_write: B|2007|input
+ <...>-2007 (-----) [006] ...1 14594.342399: tracing_mark_write: B|2007|animation
+ <...>-625 (-----) [003] ...1 14594.342447: tracing_mark_write: B|625|doTransaction
+ <...>-625 (-----) [003] ...1 14594.342489: tracing_mark_write: B|625|doTransaction
+ kworker/u17:2-1778 ( 1778) [007] d..3 14594.342532: sched_blocked_reason: pid=14607 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f8
+ kworker/u17:2-1778 ( 1778) [007] d..2 14594.342544: sched_switch: prev_comm=kworker/u17:2 prev_pid=1778 prev_prio=100 prev_state=S ==> next_comm=kworker/u16:2 next_pid=27544 next_prio=120
+ <...>-1773 (-----) [000] ...1 14594.342575: tracing_mark_write: B|625|requestNextVsync
+ <...>-1773 (-----) [000] ...1 14594.342579: tracing_mark_write: B|625|resetIdleTimer
+ <...>-27544 (-----) [007] d..2 14594.342589: sched_switch: prev_comm=kworker/u16:2 prev_pid=27544 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-656 (-----) [002] d.h3 14594.342604: sched_blocked_reason: pid=1233 iowait=0 caller=geni_i2c_xfer+0x4d8/0x1398
+ <...>-1803 (-----) [001] d..2 14594.342605: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.342632: tracing_mark_write: B|625|handleMessageInvalidate
+ <...>-625 (-----) [003] ...1 14594.342634: tracing_mark_write: B|625|handlePageFlip
+ <...>-2738 (-----) [007] ...1 14594.342641: tracing_mark_write: B|2007|notifyFramePending
+ <...>-658 (-----) [002] d..2 14594.342653: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=120
+ <...>-656 (-----) [002] ...1 14594.342656: tracing_mark_write: B|625|requestNextVsync
+ <...>-2738 (-----) [007] d..2 14594.342658: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120
+ <...>-656 (-----) [002] ...1 14594.342660: tracing_mark_write: B|625|resetIdleTimer
+ <...>-660 (-----) [005] d..2 14594.342663: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.342665: tracing_mark_write: B|625|latchBuffer
+ <...>-625 (-----) [003] ...1 14594.342673: tracing_mark_write: B|625|query
+ <...>-625 (-----) [003] ...1 14594.342682: tracing_mark_write: B|625|updateTexImage
+ <...>-625 (-----) [003] ...1 14594.342693: tracing_mark_write: B|625|acquireBuffer
+ <...>-625 (-----) [003] ...1 14594.342703: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 1
+ <...>-660 (-----) [005] d..2 14594.342709: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120
+ <...>-2007 (-----) [006] ...1 14594.342733: tracing_mark_write: B|2007|traversal
+ <...>-2007 (-----) [006] ...1 14594.342776: tracing_mark_write: B|2007|draw
+ <...>-2007 (-----) [006] ...1 14594.342791: tracing_mark_write: B|2007|Record View#draw()
+ <...>-625 (-----) [003] ...1 14594.342849: tracing_mark_write: B|625|updateInputFlinger
+ <...>-2007 (-----) [006] d..2 14594.342903: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=kworker/6:2H next_pid=24261 next_prio=100
+ <...>-2738 (-----) [007] ...1 14594.342910: tracing_mark_write: B|2007|DrawFrame
+ <...>-2738 (-----) [007] d..2 14594.342917: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98
+ <...>-24261 (-----) [006] d..2 14594.342918: sched_switch: prev_comm=kworker/6:2H prev_pid=24261 prev_prio=100 prev_state=S ==> next_comm=.android.dialer next_pid=14607 next_prio=110
+ <...>-440 (-----) [007] d..2 14594.342926: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ <...>-2738 (-----) [007] ...1 14594.342927: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [007] ...2 14594.342959: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-2738 (-----) [007] d..2 14594.342975: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-656 (-----) [007] ...1 14594.343021: tracing_mark_write: B|625|query
+ <...>-656 (-----) [007] .... 14594.343033: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-2738 (-----) [007] ...1 14594.343070: tracing_mark_write: B|2007|query
+ <...>-1233 (-----) [004] d..2 14594.343074: sched_switch: prev_comm=sound trigger c prev_pid=1233 prev_prio=120 prev_state=R+ ==> next_comm=irq/144-1436400 next_pid=2522 next_prio=49
+ <...>-2738 (-----) [007] ...2 14594.343078: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-625 (-----) [003] ...1 14594.343084: tracing_mark_write: B|625|onMessageReceived
+ <...>-625 (-----) [003] ...1 14594.343087: tracing_mark_write: B|625|handleMessageRefresh
+ <...>-625 (-----) [003] ...1 14594.343090: tracing_mark_write: B|625|preComposition
+ <...>-2738 (-----) [007] d..2 14594.343090: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-625 (-----) [003] ...1 14594.343122: tracing_mark_write: B|625|rebuildLayerStacks
+ <...>-625 (-----) [003] ...1 14594.343124: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty
+ <...>-89 (-----) [007] d..2 14594.343126: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-625 (-----) [003] ...1 14594.343129: tracing_mark_write: B|625|computeVisibleRegions
+ <...>-656 (-----) [007] ...1 14594.343136: tracing_mark_write: B|625|query
+ <...>-14607 (-----) [006] ...2 14594.343141: binder_set_priority: proc=1368 thread=3253 old=120 => new=110 desired=110
+ <...>-2965 (-----) [001] .... 14596.746610: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000002ae8fcff pfn=1522884 ofs=188416
+ <idle>-0 (-----) [002] d..2 14596.746619: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98
+ <...>-2965 (-----) [001] .... 14596.746629: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000679ee1ec pfn=1299913 ofs=192512
+ <...>-2965 (-----) [001] .... 14596.746664: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000006cd2fb7 pfn=1296251 ofs=196608
+ <...>-2965 (-----) [001] .... 14596.746677: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000af82f3d6 pfn=1419330 ofs=200704
+ <...>-2965 (-----) [001] .... 14596.746693: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000002840f054 pfn=1304928 ofs=204800
+ <...>-2965 (-----) [001] .... 14596.746706: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000004a59da17 pfn=1288069 ofs=208896
+ <...>-2965 (-----) [001] .... 14596.746717: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000023a80dca pfn=1419686 ofs=212992
+ <...>-2965 (-----) [001] .... 14596.746730: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000001cf89eab pfn=1315372 ofs=217088
+ <...>-2965 (-----) [001] .... 14596.746743: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000005b4c6cb6 pfn=1380698 ofs=221184
+ <...>-2965 (-----) [001] .... 14596.746760: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f8304ae7 pfn=1206753 ofs=225280
+ <...>-2965 (-----) [001] .... 14596.746773: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000cb912305 pfn=1325465 ofs=229376
+ <...>-2965 (-----) [001] .... 14596.746785: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f16f3774 pfn=1408056 ofs=233472
+ <...>-2965 (-----) [001] .... 14596.746801: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000056d4c926 pfn=1418352 ofs=237568
+ <...>-2965 (-----) [001] .... 14596.746815: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f3eeb42c pfn=1320957 ofs=241664
+ <...>-440 (-----) [002] d..2 14596.746916: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=swapper/2 next_pid=0 next_prio=120
+
+ <...>-656 (-----) [007] .... 14594.343145: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120
+ <...>-14607 (-----) [006] d..2 14594.343164: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-5281 (-----) [002] d..2 14594.343177: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110
+ irq/144-1436400-2522 ( 2522) [004] d..2 14594.343223: sched_switch: prev_comm=irq/144-1436400 prev_pid=2522 prev_prio=49 prev_state=D ==> next_comm=sound trigger c next_pid=1233 next_prio=120
+ <...>-88 (-----) [006] d..2 14594.343240: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-1238 (-----) [001] d..2 14594.343243: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-2738 (-----) [002] ...1 14594.343244: tracing_mark_write: B|2007|syncFrameState
+ <...>-2738 (-----) [002] ...1 14594.343293: tracing_mark_write: B|2007|prepareTree
+ <...>-1695 (-----) [001] d..2 14594.343318: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96
+ <...>-5281 (-----) [005] d..2 14594.343322: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:1368_14 next_pid=3253 next_prio=110
+ <...>-1238 (-----) [001] d..2 14594.343442: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=InputDispatcher next_pid=1695 next_prio=112
+ <...>-1695 (-----) [001] d..2 14594.343467: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
+ <...>-5281 (-----) [000] d..2 14594.343484: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120
+ <...>-625 (-----) [003] ...1 14594.343519: tracing_mark_write: B|625|calculateWorkingSet
+ <...>-2738 (-----) [002] ...1 14594.343568: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343577: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343586: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343591: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343597: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343602: tracing_mark_write: B|2007|query
+ <...>-2738 (-----) [002] ...1 14594.343609: tracing_mark_write: B|2007|dequeueBuffer
+ <...>-2007 (-----) [006] d..2 14594.343612: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120
+ <...>-2738 (-----) [002] ...2 14594.343633: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110
+ <...>-2738 (-----) [002] d..2 14594.343683: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110
+ <...>-625 (-----) [003] ...1 14594.343704: tracing_mark_write: B|625|prepare
+ <...>-656 (-----) [002] ...1 14594.343707: tracing_mark_write: B|625|dequeueBuffer
+ <...>-625 (-----) [004] ...1 14594.812869: tracing_mark_write: B|625|com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity#0: 2
+ <...>-2048 (-----) [000] d..2 14594.812895: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:625_3 next_pid=1431 next_prio=120
+ <...>-1431 (-----) [000] ...1 14594.812911: tracing_mark_write: B|625|query
+ <...>-625 (-----) [004] ...1 14594.812914: tracing_mark_write: B|625|latchBuffer
+ <...>-625 (-----) [004] ...1 14594.812919: tracing_mark_write: B|625|query
+ <...>-625 (-----) [004] ...1 14594.812925: tracing_mark_write: B|625|updateTexImage
+ <...>-625 (-----) [004] ...1 14594.812928: tracing_mark_write: B|625|acquireBuffer
+ <...>-625 (-----) [004] ...1 14594.812934: tracing_mark_write: B|625|StatusBar#0: 1
+ <...>-2048 (-----) [000] ...1 14594.812962: tracing_mark_write: B|1803|syncFrameState
+ <...>-656 (-----) [002] ...1 14594.813044: tracing_mark_write: B|625|setTransactionState
+ <...>-14607 (-----) [007] ...2 14594.813083: binder_set_priority: proc=10691 thread=18733 old=120 => new=110 desired=110
+ <...>-14607 (-----) [007] d..2 14594.813114: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=kworker/7:1 next_pid=7092 next_prio=120
+ <...>-14655 (-----) [006] d..2 14594.813128: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=lpass_smem_glin next_pid=89 next_prio=98
+ <...>-89 (-----) [006] d..2 14594.813163: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130
+ <...>-656 (-----) [002] ...1 14594.813218: tracing_mark_write: B|625|requestNextVsync
+ <...>-656 (-----) [002] ...1 14594.813222: tracing_mark_write: B|625|resetIdleTimer
+ kworker/7:1-7092 ( 7092) [007] d..2 14594.813239: sched_switch: prev_comm=kworker/7:1 prev_pid=7092 prev_prio=120 prev_state=R+ ==> next_comm=smem_native_lpa next_pid=88 next_prio=98
+ <...>-5281 (-----) [001] d..2 14594.813245: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:10691_B next_pid=18733 next_prio=110
+ <...>-88 (-----) [007] d..2 14594.813248: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=R ==> next_comm=kgsl_worker_thr next_pid=259 next_prio=97
+ <...>-2048 (-----) [000] d..2 14594.813249: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96
+ <...>-14655 (-----) [006] d..2 14594.813263: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R+ ==> next_comm=smem_native_lpa next_pid=88 next_prio=98
+ <...>-661 (-----) [002] d..2 14594.813265: sched_switch: prev_comm=sf prev_pid=661 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116
+ <...>-259 (-----) [007] d..2 14594.813265: sched_switch: prev_comm=kgsl_worker_thr prev_pid=259 prev_prio=97 prev_state=S ==> next_comm=kworker/7:1 next_pid=7092 next_prio=120
+ kworker/7:1-7092 ( 7092) [007] d..2 14594.813271: sched_switch: prev_comm=kworker/7:1 prev_pid=7092 prev_prio=120 prev_state=S ==> next_comm=system next_pid=108 next_prio=120
+ <...>-108 (-----) [007] .... 14594.813275: ion_heap_shrink: heap_name=system, len=9469952, total_allocated=189620224
+ <...>-88 (-----) [006] d..2 14594.813294: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130
+ <...>-625 (-----) [004] ...1 14594.813310: tracing_mark_write: B|625|updateInputFlinger
+ <...>-1238 (-----) [000] d..2 14594.813312: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120
+ <...>-661 (-----) [002] d..2 14594.813317: sched_switch: prev_comm=sf prev_pid=661 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116
+ <...>-14640 (-----) [005] d..2 14594.813319: sched_switch: prev_comm=DialerExecutors prev_pid=14640 prev_prio=130 prev_state=R ==> next_comm=DispSync next_pid=658 next_prio=97
+ <...>-656 (-----) [002] ...1 14594.813336: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-658 (-----) [005] d..2 14594.813345: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=DialerExecutors next_pid=14640 next_prio=130
+ <...>-656 (-----) [002] ...1 14594.813345: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813353: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-2048 (-----) [000] d..2 14594.813358: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96
+ <...>-656 (-----) [002] ...1 14594.813364: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-5281 (-----) [001] d..2 14594.813369: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:10691_B next_pid=18733 next_prio=110
+ <...>-656 (-----) [002] ...1 14594.813372: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813380: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813391: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813398: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813408: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813416: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813424: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813432: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] .n.1 14594.813443: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-1238 (-----) [000] d..2 14594.813464: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120
+ <...>-5281 (-----) [002] d..2 14594.813525: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116
+ <...>-656 (-----) [002] ...1 14594.813544: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-656 (-----) [002] ...1 14594.813557: tracing_mark_write: B|625|~GraphicBuffer
+ <...>-2048 (-----) [000] d..2 14594.813594: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:1368_15 next_pid=3359 next_prio=120
+ <...>-18733 (-----) [001] ...2 14594.813635: binder_set_priority: proc=1368 thread=3514 old=120 => new=110 desired=110
+ <...>-656 (-----) [002] .... 14594.813637: binder_set_priority: proc=625 thread=656 old=116 => new=120 desired=120
+ <...>-108 (-----) [007] d..2 14594.813646: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=116
+ <...>-625 (-----) [004] ...1 14594.813646: tracing_mark_write: B|625|onMessageReceived
+ <...>-625 (-----) [004] ...1 14594.813649: tracing_mark_write: B|625|handleMessageRefresh
+ <...>-625 (-----) [004] ...1 14594.813651: tracing_mark_write: B|625|preComposition
+ <...>-625 (-----) [004] ...1 14594.813693: tracing_mark_write: B|625|rebuildLayerStacks
+ <...>-625 (-----) [004] ...1 14594.813696: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty
+ <...>-625 (-----) [004] ...1 14594.813701: tracing_mark_write: B|625|computeVisibleRegions
+ <...>-1398 (-----) [007] d..2 14594.813718: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=116 prev_state=S ==> next_comm=system next_pid=108 next_prio=120
+ <...>-108 (-----) [007] d..2 14594.813739: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=116
+ <...>-1695 (-----) [002] d..2 14594.813970: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=system next_pid=108 next_prio=120
+ <...>-1398 (-----) [007] ...1 14594.814029: tracing_mark_write: B|1368|wmLayout
+ <...>-1398 (-----) [007] ...1 14594.814033: tracing_mark_write: B|1368|performSurfacePlacement
+ <...>-1398 (-----) [007] ...1 14594.814040: tracing_mark_write: B|1368|applySurfaceChanges
+ <...>-1398 (-----) [007] ...1 14594.814043: tracing_mark_write: B|1368|openSurfaceTransaction
+ <...>-1398 (-----) [007] ...1 14594.814063: tracing_mark_write: B|1368|performLayout
+ <...>-625 (-----) [004] ...1 14594.814119: tracing_mark_write: B|625|calculateWorkingSet
+ <...>-1398 (-----) [007] ...1 14594.814241: tracing_mark_write: B|1368|layoutInputConsumer
+ <...>-2048 (-----) [000] ...1 14594.814260: tracing_mark_write: B|1803|prepareTree
+ <...>-1398 (-----) [007] ...1 14594.814263: tracing_mark_write: B|1368|applyPostLayoutPolicy
+ <...>-2048 (-----) [000] d..2 14594.814408: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R ==> next_comm=Binder:1368_15 next_pid=3359 next_prio=120
+ <...>-625 (-----) [004] ...1 14594.814411: tracing_mark_write: B|625|prepare
+ <...>-625 (-----) [004] ...1 14594.814428: tracing_mark_write: B|625|HIDL::IComposerClient::executeCommands_2_2::client
+ <...>-2048 (-----) [000] d..2 14594.814533: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120
+ <...>-1803 (-----) [000] d..2 14594.814558: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120
+ <...>-2048 (-----) [000] d..2 14594.814572: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120
+ <...>-625 (-----) [004] ...2 14594.814589: binder_set_priority: proc=627 thread=627 old=97 => new=98 desired=98
+ <...>-108 (-----) [002] d..2 14594.814650: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=composer@2.2-se next_pid=627 next_prio=98
+ <...>-625 (-----) [004] d..2 14594.814664: sched_switch: prev_comm=surfaceflinger prev_pid=625 prev_prio=98 prev_state=S ==> next_comm=ashmemd next_pid=854 next_prio=129
+ <...>-1398 (-----) [007] ...1 14594.814723: tracing_mark_write: B|1368|applyWindowSurfaceChanges
+ <...>-854 (-----) [004] .... 14594.814746: binder_set_priority: proc=854 thread=854 old=129 => new=120 desired=120
+ <...>-854 (-----) [004] d..2 14594.814757: sched_switch: prev_comm=ashmemd prev_pid=854 prev_prio=120 prev_state=R+ ==> next_comm=highpool[0] next_pid=3493 next_prio=129
+ <...>-1803 (-----) [000] d..2 14594.814763: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120
+ <...>-18733 (-----) [001] d..1 14594.814819: mm_filemap_delete_from_page_cache: dev 0:1 ino 3ce5e7 page=0000000083f10c7a pfn=1298474 ofs=0
+ <...>-2048 (-----) [000] ...1 14594.814842: tracing_mark_write: B|1803|dequeueBuffer
+ <...>-1398 (-----) [007] ...1 14594.814850: tracing_mark_write: F|1368|launching: com.google.android.dialer|0
+ <...>-1398 (-----) [007] ...1 14594.814855: tracing_mark_write: B|1368|MetricsLogger:launchObserverNotifyActivityLaunchFinished
+ <...>-1398 (-----) [007] ...1 14594.814857: tracing_mark_write: B|1368|MetricsLogger:convertActivityRecordToProto
+ <...>-2048 (-----) [000] d..2 14594.814905: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=120
+ <...>-1410 (-----) [006] .... 14592.997816: mm_filemap_add_to_page_cache: dev 253:6 ino b785 page=00000000615a8f24 pfn=1134764 ofs=0
+ <...>-1410 (-----) [006] .... 14592.997831: mm_filemap_add_to_page_cache: dev 253:6 ino b785 page=000000008768a58f pfn=1134751 ofs=4096
+
+ <...>-18733 (-----) [001] .... 14594.814914: binder_set_priority: proc=10691 thread=18733 old=110 => new=120 desired=120
+ <...>-14655 (-----) [006] d..2 14594.814932: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=.android.dialer next_pid=14607 next_prio=110
+ <...>-656 (-----) [000] ...1 14594.814948: tracing_mark_write: B|625|dequeueBuffer
+ <...>-3514 (-----) [001] .... 14594.814954: binder_set_priority: proc=1368 thread=3514 old=110 => new=120 desired=120
+ <...>-656 (-----) [000] ...1 14594.814963: tracing_mark_write: B|625|NavigationBar0#0: 2
+ <...>-14607 (-----) [006] ...2 14594.815022: binder_set_priority: proc=1368 thread=3514 old=120 => new=110 desired=110
+ <...>-1398 (-----) [007] ...1 14594.815039: tracing_mark_write: B|1368|prepareSurfaces
+ <...>-14607 (-----) [006] d..2 14594.815041: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130
+ <...>-3493 (-----) [004] d..2 14594.815057: sched_switch: prev_comm=highpool[0] prev_pid=3493 prev_prio=129 prev_state=R ==> next_comm=Binder:1368_18 next_pid=3514 next_prio=110
+ <...>-2048 (-----) [000] ...1 14594.815088: tracing_mark_write: B|1803|HWC release fence 45750 has signaled
+ <...>-2048 (-----) [000] ...1 14594.815119: tracing_mark_write: B|1803|eglBeginFrame
+ <...>-14655 (-----) [006] d..2 14594.815190: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=crtc_commit:97 next_pid=301 next_prio=83
+ <...>-3514 (-----) [004] .... 14594.815193: binder_set_priority: proc=1368 thread=3514 old=110 => new=120 desired=120
+ <...>-1398 (-----) [007] ...1 14594.815322: tracing_mark_write: B|1368|closeSurfaceTransaction
+ <...>-3493 (-----) [004] .... 14594.815353: mm_filemap_add_to_page_cache: dev 253:6 ino 113b page=0000000069e2b98a pfn=628464 ofs=2723840
+ <...>-1398 (-----) [007] ...2 14594.815393: binder_set_priority: proc=625 thread=656 old=120 => new=116 desired=116
+ rcu_sched-8 ( 8) [007] d..2 14594.815449: sched_switch: prev_comm=rcu_sched prev_pid=8 prev_prio=120 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116
diff --git a/startop/scripts/trace_analyzer/trace_analyzer b/startop/scripts/trace_analyzer/trace_analyzer
new file mode 100755
index 0000000..8c03964
--- /dev/null
+++ b/startop/scripts/trace_analyzer/trace_analyzer
@@ -0,0 +1,42 @@
+#!/bin/bash
+# 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.
+
+DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+
+if [[ "$#" -lt 2 ]]; then
+ echo "Usage: $0 <filename.trace> <sqlite-filename.db>" >&2
+ exit 1
+fi
+
+TRACE_FILENAME="$1"
+SQLITE_FILENAME="$2"
+
+#echo "Trace filename: $TRACE_FILENAME"
+#echo "SQLite filename: $SQLITE_FILENAME"
+
+if ! [[ -f "$TRACE_FILENAME" ]]; then
+ echo "Error: Trace '$TRACE_FILENAME' does not exist." >&2
+ exit 1
+fi
+
+if ! "$DIR/trace_analyzer.py" "$SQLITE_FILENAME" "$TRACE_FILENAME" > /dev/null; then
+ echo "Fatal: trace_analyzer.py failed, aborting." >&2
+ exit 1
+fi
+
+if ! "$DIR/run-sql-queries" "$SQLITE_FILENAME"; then
+ echo "Fatal: Failed to run sql queries, aborting." >&2
+ exit 1
+fi
diff --git a/startop/scripts/trace_analyzer/trace_analyzer.py b/startop/scripts/trace_analyzer/trace_analyzer.py
new file mode 100755
index 0000000..62ae018
--- /dev/null
+++ b/startop/scripts/trace_analyzer/trace_analyzer.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python3
+# 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.
+
+import re
+import sys
+import argparse
+
+from lib.trace2db import Trace2Db
+
+# This script requires 'sqlalchemy' to access the sqlite3 database.
+#
+# $> sudo apt-get install python3-pip
+# $> pip3 install --user sqlalchemy
+#
+
+def main(argv):
+ parser = argparse.ArgumentParser(description='Convert ftrace/systrace file into sqlite3 db.')
+ parser.add_argument('db_filename', metavar='sql_filename.db', type=str,
+ help='path to sqlite3 db filename')
+ parser.add_argument('trace_filename', metavar='systrace.ftrace', type=str,
+ help='path to ftrace/systrace filename')
+ parser.add_argument('--limit', type=int, help='limit the number of entries parsed [for debugging]')
+
+ args = parser.parse_args()
+
+ db_filename = args.db_filename
+ trace_filename = args.trace_filename
+
+ trace2db = Trace2Db(db_filename)
+ print("SQL Alchemy db initialized")
+
+ # parse 'raw_ftrace_entries' table
+ count = trace2db.parse_file_into_db(trace_filename, limit=args.limit)
+ print("Count was ", count)
+
+ return 0
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/startop/scripts/trace_analyzer/trace_analyzer_recursive b/startop/scripts/trace_analyzer/trace_analyzer_recursive
new file mode 100755
index 0000000..4d9ee0e
--- /dev/null
+++ b/startop/scripts/trace_analyzer/trace_analyzer_recursive
@@ -0,0 +1,78 @@
+#!/bin/bash
+# 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.
+
+DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+
+if [[ "$#" -lt 3 ]]; then
+ echo "Usage: $0 <trace-dir> <db-dir> <output.csv>" >&2
+ exit 1
+fi
+
+simulate="n"
+
+TRACE_DIRNAME="$1"
+SQLITE_DIRNAME="$2"
+OUTPUT_FILENAME="$3"
+
+echo "Trace filename: $TRACE_DIRNAME"
+echo "SQLite filename: $SQLITE_DIRNAME"
+
+if ! [[ -d "$TRACE_DIRNAME" ]]; then
+ echo "Error: Trace '$TRACE_DIRNAME' does not exist." >&2
+ exit 1
+fi
+
+process_trace_file() {
+ local trace_filename="$1"
+ local db_dirname="$2"
+ local output_file="$3"
+
+ local db_filename="$db_dirname/$(basename "$trace_filename").db"
+
+ if [[ $simulate == y ]]; then
+ echo "$DIR/trace_analyzer.py" "$db_filename" "$trace_filename" "> /dev/null"
+ else
+ if ! "$DIR/trace_analyzer.py" "$db_filename" "$trace_filename" > /dev/null; then
+ echo "Fatal: trace_analyzer.py failed, aborting." >&2
+ return 1
+ fi
+ fi
+
+ if [[ $simulate == y ]]; then
+ echo "$DIR/run-sql-queries" "$db_filename" ">> '$output_file'"
+ else
+ # append name of trace to CSV, so we can see where data came from
+ echo "; $trace_filename" >> "$output_file"
+ if ! "$DIR/run-sql-queries" "$db_filename" >> "$output_file"; then
+ echo "Fatal: Failed to run sql queries, aborting." >&2
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+find "$TRACE_DIRNAME" -type f -name '*.trace' -print0 |
+while IFS= read -r -d '' file; do
+ if [[ $file == *#*.trace && $file != *#1.trace ]]; then
+ echo "Skip $file"
+ continue
+ fi
+
+ printf '%s\n' "$file"
+ process_trace_file "$file" "$SQLITE_DIRNAME" "$OUTPUT_FILENAME"
+done
+
+echo "Done"
diff --git a/startop/scripts/trace_analyzer/trace_analyzer_test.py b/startop/scripts/trace_analyzer/trace_analyzer_test.py
new file mode 100644
index 0000000..579529c
--- /dev/null
+++ b/startop/scripts/trace_analyzer/trace_analyzer_test.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+#
+
+"""
+Unit tests for trace_analyzer module.
+
+Install:
+ $> sudo apt-get install python3-pytest ## OR
+ $> pip install -U pytest
+See also https://docs.pytest.org/en/latest/getting-started.html
+
+Usage:
+ $> pytest trace_analyzer_test.py
+
+See also https://docs.pytest.org/en/latest/usage.html
+"""
+
+# global imports
+import os
+import sys
+
+DIR = os.path.abspath(os.path.dirname(__file__))
+
+sys.path.append(os.path.dirname(DIR))
+import lib.cmd_utils as cmd_utils
+
+def test_trace_analyzer(tmpdir):
+ # Setup
+ bin = os.path.join(DIR, 'trace_analyzer')
+ systrace = os.path.join(DIR, 'test_fixtures/common_systrace')
+ db_file = tmpdir.mkdir('trace_analyzer').join('test.db')
+
+ # Act
+ passed, output = cmd_utils.execute_arbitrary_command([bin, systrace,
+ str(db_file)],
+ timeout=300,
+ shell=False,
+ simulate=False)
+
+ # Assert
+ assert passed
+ assert output == """\
+'blocked_iowait_duration_ms',\
+'process_name',\
+'launching_duration_ms',\
+'launching_started_timestamp_ms',\
+'launching_finished_timestamp_ms'
+81.697999999960302375,\
+'com.google.android.dialer',\
+594.99400000095192808,\
+14594219.85600000061,\
+14594814.85000000149"""