services: Add iorap forwarding service

Add a new service to system_server. It purely forwards
data from internal APIs to iorapd over binder, it is not a binder
service itself.

Currently forwards ActivityMetricsLaunchObserver APIs from
ActivityTaskManagerInternal.

Bug: 72170747
Change-Id: Ic4fa283df1c16660099030c74a0039ef24866819
This commit is contained in:
Igor Murashkin
2018-11-26 10:33:17 -08:00
parent ffdf092a77
commit 4de1e16570
8 changed files with 627 additions and 5 deletions

View File

@@ -28,6 +28,7 @@ java_library {
"services.net",
"services.print",
"services.restrictions",
"services.startop",
"services.usage",
"services.usb",
"services.voiceinteraction",

View File

@@ -145,6 +145,7 @@ import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowManagerGlobalLock;
import com.android.server.wm.WindowManagerService;
import com.google.android.startop.iorap.IorapForwardingService;
import dalvik.system.VMRuntime;
@@ -1007,10 +1008,13 @@ public final class SystemServer {
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
traceBeginAndSlog("IorapForwardingService");
mSystemServiceManager.startService(IorapForwardingService.class);
traceEnd();
traceBeginAndSlog("SignedConfigService");
SignedConfigService.registerUpdateReceiver(mSystemContext);
traceEnd();
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);

View File

@@ -0,0 +1,24 @@
/*
* 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: "services.startop",
static_libs: [
// frameworks/base/startop/iorap
"services.startop.iorap",
],
}

View File

@@ -13,7 +13,7 @@
// limitations under the License.
java_library_static {
name: "libiorap-java",
name: "services.startop.iorap",
aidl: {
include_dirs: [
@@ -21,6 +21,8 @@ java_library_static {
],
},
libs: ["services.core"],
srcs: [
":iorap-aidl",
"**/*.java",

View File

@@ -0,0 +1,368 @@
/*
* 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;
// TODO: fix this. either move this class into system server or add a dependency on
// these wm classes to libiorap-java and libiorap-java-tests (somehow).
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.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 IntentStarted(@SequenceId long sequenceId, Intent intent) {
super(sequenceId);
this.intent = intent;
Objects.requireNonNull(intent, "intent");
}
@Override
public boolean equals(Object other) {
if (other instanceof IntentStarted) {
return intent.equals(((IntentStarted)other).intent) &&
super.equals(other);
}
return false;
}
@Override
protected String toStringBody() {
return ", intent=" + intent.toString();
}
@Override
protected void writeToParcelImpl(Parcel p, int flags) {
super.writeToParcelImpl(p, flags);
intent.writeToParcel(p, flags);
}
IntentStarted(Parcel p) {
super(p);
intent = Intent.CREATOR.createFromParcel(p);
}
}
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 activityRecordSnapshot.equals(
((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
super.equals(other);
}
return false;
}
@Override
protected String toStringBody() {
return ", " + activityRecordSnapshot.toString();
}
@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 ", 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 ActivityLaunchFinished(@SequenceId long sequenceId,
@NonNull @ActivityRecordProto byte[] snapshot) {
super(sequenceId, snapshot);
}
@Override
public boolean equals(Object other) {
if (other instanceof ActivityLaunched) {
return super.equals(other);
}
return false;
}
}
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 Objects.equals(activityRecordSnapshot,
((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
super.equals(other);
}
return false;
}
@Override
protected String toStringBody() {
return ", " + activityRecordSnapshot.toString();
}
@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;
}
}
}
@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,
};
// TODO: move to @ActivityRecordProto byte[] once we have unit tests.
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;
}
}
}

View File

@@ -0,0 +1,216 @@
/*
* 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.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
/**
* System-server-local proxy into the {@code IIorap} native service.
*/
public class IorapForwardingService extends SystemService {
public static final boolean DEBUG = true; // TODO: read from a getprop?
public static final String TAG = "IorapForwardingService";
private IIorap mIorapRemote;
/**
* 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);
}
//<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() {
try {
return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
} catch (ServiceManager.ServiceNotFoundException e) {
// TODO: how do we handle service being missing?
throw new AssertionError(e);
}
}
//</editor-fold>
@Override
public void onStart() {
if (DEBUG) {
Log.v(TAG, "onStart");
}
// Connect to the native binder service.
mIorapRemote = provideIorapRemote();
invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
// Listen to App Launch Sequence events from ActivityTaskManager,
// and forward them to the native binder service.
ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
provideLaunchObserverRegistry();
launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver());
}
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;
@Override
public void onIntentStarted(@NonNull Intent intent) {
// #onIntentStarted [is the only transition that] initiates a new launch sequence.
++mSequenceId;
if (DEBUG) {
Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
mSequenceId, intent));
}
invokeRemote(() ->
mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
new AppLaunchEvent.IntentStarted(mSequenceId, intent))
);
}
@Override
public void onIntentFailed() {
if (DEBUG) {
Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
}
invokeRemote(() ->
mIorapRemote.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.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.onAppLaunchEvent(RequestId.nextValueForSequence(),
new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
activity)));
}
@Override
public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
if (DEBUG) {
Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
mSequenceId, activity));
}
invokeRemote(() ->
mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity))
);
}
}
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));
}
// TODO: implement rest.
}
}
private interface RemoteRunnable {
void run() throws RemoteException;
}
private static void invokeRemote(RemoteRunnable r) {
try {
r.run();
} catch (RemoteException e) {
// TODO: what do we do with exceptions?
throw new AssertionError("not implemented", e);
}
}
}

View File

@@ -71,7 +71,7 @@ public class RequestId implements Parcelable {
@Override
public String toString() {
return String.format("{requestId: %ld}", requestId);
return String.format("{requestId: %d}", requestId);
}
@Override

View File

@@ -18,8 +18,15 @@ java_library {
srcs: ["src/**/*.kt"],
static_libs: [
// non-test dependencies
"libiorap-java",
// 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",
"android-support-test",