Merge "startop: Add iorap parcelables for AIDL interfaces"

This commit is contained in:
Treehugger Robot
2018-10-04 01:13:47 +00:00
committed by Gerrit Code Review
12 changed files with 1266 additions and 0 deletions

51
startop/iorap/Android.bp Normal file
View File

@@ -0,0 +1,51 @@
// 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.
java_library_static {
name: "libiorap-java",
aidl: {
include_dirs: [
"system/iorap/binder",
],
},
srcs: [
":iorap-aidl",
"**/*.java",
],
}
android_test {
name: "libiorap-java-tests",
srcs: ["tests/src/**/*.kt"],
static_libs: [
// non-test dependencies
"libiorap-java",
// test android dependencies
"platform-test-annotations",
"android-support-test",
// test framework dependencies
"mockito-target-inline-minus-junit4",
"truth-prebuilt",
],
//sdk_version: "current",
//certificate: "platform",
libs: ["android.test.base"],
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,31 @@
<?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="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.google.android.startop.iorap.tests" />
<application>
<uses-library android:name="android.test.runner" />
</application>
</manifest>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,120 @@
/*
* 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: %ld}", 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>
}

View File

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

View File

@@ -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#onStartUser */
public static final int TYPE_START_USER = 0;
/** @see com.android.server.SystemService#onUnlockUser */
public static final int TYPE_UNLOCK_USER = 1;
/** @see com.android.server.SystemService#onSwitchUser*/
public static final int TYPE_SWITCH_USER = 2;
/** @see com.android.server.SystemService#onStopUser */
public static final int TYPE_STOP_USER = 3;
/** @see com.android.server.SystemService#onCleanupUser */
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>
}

View File

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

View File

@@ -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 android.support.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).isNotSameAs(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)
}
}
}