Merge "Deliver start service args with ParcelledListSlice." into oc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d1d462c..7299d6b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.AssetManager;
@@ -869,16 +870,20 @@
sendMessage(H.UNBIND_SERVICE, s);
}
- public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags ,Intent args) {
- ServiceArgsData s = new ServiceArgsData();
- s.token = token;
- s.taskRemoved = taskRemoved;
- s.startId = startId;
- s.flags = flags;
- s.args = args;
+ public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
+ List<ServiceStartArgs> list = args.getList();
- sendMessage(H.SERVICE_ARGS, s);
+ for (int i = 0; i < list.size(); i++) {
+ ServiceStartArgs ssa = list.get(i);
+ ServiceArgsData s = new ServiceArgsData();
+ s.token = token;
+ s.taskRemoved = ssa.taskRemoved;
+ s.startId = ssa.startId;
+ s.flags = ssa.flags;
+ s.args = ssa.args;
+
+ sendMessage(H.SERVICE_ARGS, s);
+ }
}
public final void scheduleStopService(IBinder token) {
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 6c43fe3..1b3c00b 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
@@ -86,8 +87,7 @@
in Bundle coreSettings, in String buildSerial);
void scheduleExit();
void scheduleConfigurationChanged(in Configuration config);
- void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
- int flags, in Intent args);
+ void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
void updateTimeZone();
void processInBackground();
void scheduleBindService(IBinder token,
diff --git a/core/java/android/app/ServiceStartArgs.aidl b/core/java/android/app/ServiceStartArgs.aidl
new file mode 100644
index 0000000..fd2cf0f
--- /dev/null
+++ b/core/java/android/app/ServiceStartArgs.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+/** @hide */
+parcelable ServiceStartArgs;
diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java
new file mode 100644
index 0000000..f030cba
--- /dev/null
+++ b/core/java/android/app/ServiceStartArgs.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes a Service.onStartCommand() request from the system.
+ * @hide
+ */
+public class ServiceStartArgs implements Parcelable {
+ final public boolean taskRemoved;
+ final public int startId;
+ final public int flags;
+ final public Intent args;
+
+ public ServiceStartArgs(boolean _taskRemoved, int _startId, int _flags, Intent _args) {
+ taskRemoved = _taskRemoved;
+ startId = _startId;
+ flags = _flags;
+ args = _args;
+ }
+
+ public String toString() {
+ return "ServiceStartArgs{taskRemoved=" + taskRemoved + ", startId=" + startId
+ + ", flags=0x" + Integer.toHexString(flags) + ", args=" + args + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(taskRemoved ? 1 : 0);
+ out.writeInt(startId);
+ out.writeInt(flags);
+ if (args != null) {
+ out.writeInt(1);
+ args.writeToParcel(out, 0);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<ServiceStartArgs> CREATOR
+ = new Parcelable.Creator<ServiceStartArgs>() {
+ public ServiceStartArgs createFromParcel(Parcel in) {
+ return new ServiceStartArgs(in);
+ }
+
+ public ServiceStartArgs[] newArray(int size) {
+ return new ServiceStartArgs[size];
+ }
+ };
+
+ public ServiceStartArgs(Parcel in) {
+ taskRemoved = in.readInt() != 0;
+ startId = in.readInt();
+ flags = in.readInt();
+ if (in.readInt() != 0) {
+ args = Intent.CREATOR.createFromParcel(in);
+ } else {
+ args = null;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index c4e4e06..aaa5f19c 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -50,6 +50,8 @@
private final List<T> mList;
+ private int mInlineCountLimit = Integer.MAX_VALUE;
+
public BaseParceledListSlice(List<T> list) {
mList = list;
}
@@ -135,6 +137,14 @@
}
/**
+ * Set a limit on the maximum number of entries in the array that will be included
+ * inline in the initial parcelling of this object.
+ */
+ public void setInlineCountLimit(int maxCount) {
+ mInlineCountLimit = maxCount;
+ }
+
+ /**
* Write this to another Parcel. Note that this discards the internal Parcel
* and should not be used anymore. This is so we can pass this to a Binder
* where we won't have a chance to call recycle on this.
@@ -149,7 +159,7 @@
final Class<?> listElementClass = mList.get(0).getClass();
writeParcelableCreator(mList.get(0), dest);
int i = 0;
- while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
+ while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
dest.writeInt(1);
final T parcelable = mList.get(i);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4cbfb27..bebcd4a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -31,8 +31,10 @@
import android.app.ActivityThread;
import android.app.AppOpsManager;
+import android.app.ServiceStartArgs;
import android.content.IIntentSender;
import android.content.IntentSender;
+import android.content.pm.ParceledListSlice;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -1956,78 +1958,86 @@
return;
}
- while (r.pendingStarts.size() > 0) {
- Exception caughtException = null;
- ServiceRecord.StartItem si = null;
- try {
- si = r.pendingStarts.remove(0);
- if (DEBUG_SERVICE) {
- Slog.v(TAG_SERVICE, "Sending arguments to: "
- + r + " " + r.intent + " args=" + si.intent);
- }
- if (si.intent == null && N > 1) {
- // If somehow we got a dummy null intent in the middle,
- // then skip it. DO NOT skip a null intent when it is
- // the only one in the list -- this is to support the
- // onStartCommand(null) case.
- continue;
- }
- si.deliveredTime = SystemClock.uptimeMillis();
- r.deliveredStarts.add(si);
- si.deliveryCount++;
- if (si.neededGrants != null) {
- mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
- si.getUriPermissionsLocked());
- }
- mAm.grantEphemeralAccessLocked(r.userId, si.intent,
- r.appInfo.uid, UserHandle.getAppId(si.callingId));
- bumpServiceExecutingLocked(r, execInFg, "start");
- if (!oomAdjusted) {
- oomAdjusted = true;
- mAm.updateOomAdjLocked(r.app);
- }
- if (r.fgRequired && !r.fgWaiting) {
- if (!r.isForeground) {
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
- }
- scheduleServiceForegroundTransitionTimeoutLocked(r);
- } else {
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Service already foreground; no new timeout: " + r);
- }
- r.fgRequired = false;
- }
- }
- int flags = 0;
- if (si.deliveryCount > 1) {
- flags |= Service.START_FLAG_RETRY;
- }
- if (si.doneExecutingCount > 0) {
- flags |= Service.START_FLAG_REDELIVERY;
- }
- r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
- } catch (TransactionTooLargeException e) {
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
- + si.intent);
- caughtException = e;
- } catch (RemoteException e) {
- // Remote process gone... we'll let the normal cleanup take care of this.
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
- caughtException = e;
- } catch (Exception e) {
- Slog.w(TAG, "Unexpected exception", e);
- caughtException = e;
- }
+ ArrayList<ServiceStartArgs> args = new ArrayList<>();
- if (caughtException != null) {
- // Keep nesting count correct
- final boolean inDestroying = mDestroyingServices.contains(r);
- serviceDoneExecutingLocked(r, inDestroying, inDestroying);
- if (caughtException instanceof TransactionTooLargeException) {
- throw (TransactionTooLargeException)caughtException;
+ while (r.pendingStarts.size() > 0) {
+ ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ }
+ if (si.intent == null && N > 1) {
+ // If somehow we got a dummy null intent in the middle,
+ // then skip it. DO NOT skip a null intent when it is
+ // the only one in the list -- this is to support the
+ // onStartCommand(null) case.
+ continue;
+ }
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
+ if (si.neededGrants != null) {
+ mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+ si.getUriPermissionsLocked());
+ }
+ mAm.grantEphemeralAccessLocked(r.userId, si.intent,
+ r.appInfo.uid, UserHandle.getAppId(si.callingId));
+ bumpServiceExecutingLocked(r, execInFg, "start");
+ if (!oomAdjusted) {
+ oomAdjusted = true;
+ mAm.updateOomAdjLocked(r.app);
+ }
+ if (r.fgRequired && !r.fgWaiting) {
+ if (!r.isForeground) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
+ }
+ scheduleServiceForegroundTransitionTimeoutLocked(r);
+ } else {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service already foreground; no new timeout: " + r);
+ }
+ r.fgRequired = false;
}
- break;
+ }
+ int flags = 0;
+ if (si.deliveryCount > 1) {
+ flags |= Service.START_FLAG_RETRY;
+ }
+ if (si.doneExecutingCount > 0) {
+ flags |= Service.START_FLAG_REDELIVERY;
+ }
+ args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
+ }
+
+ ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
+ slice.setInlineCountLimit(4);
+ Exception caughtException = null;
+ try {
+ r.app.thread.scheduleServiceArgs(r, slice);
+ } catch (TransactionTooLargeException e) {
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+ + " args, first: " + args.get(0).args);
+ Slog.w(TAG, "Failed delivering service starts", e);
+ caughtException = e;
+ } catch (RemoteException e) {
+ // Remote process gone... we'll let the normal cleanup take care of this.
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
+ Slog.w(TAG, "Failed delivering service starts", e);
+ caughtException = e;
+ } catch (Exception e) {
+ Slog.w(TAG, "Unexpected exception", e);
+ caughtException = e;
+ }
+
+ if (caughtException != null) {
+ // Keep nesting count correct
+ final boolean inDestroying = mDestroyingServices.contains(r);
+ for (int i = 0; i < args.size(); i++) {
+ serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+ }
+ if (caughtException instanceof TransactionTooLargeException) {
+ throw (TransactionTooLargeException)caughtException;
}
}
}