Merge "Deliver start service args with ParcelledListSlice." into oc-dev

This commit is contained in:
Dianne Hackborn
2017-04-05 20:37:31 +00:00
committed by Android (Google) Code Review
6 changed files with 209 additions and 82 deletions

View File

@@ -38,6 +38,7 @@ import android.content.pm.InstrumentationInfo;
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 @@ public final class ActivityThread {
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) {

View File

@@ -25,6 +25,7 @@ import android.content.IIntentReceiver;
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 @@ oneway interface IApplicationThread {
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,

View File

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

View File

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

View File

@@ -50,6 +50,8 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
private final List<T> mList;
private int mInlineCountLimit = Integer.MAX_VALUE;
public BaseParceledListSlice(List<T> list) {
mList = list;
}
@@ -134,6 +136,14 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
return mList;
}
/**
* 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
@@ -149,7 +159,7 @@ abstract class BaseParceledListSlice<T> implements Parcelable {
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);

View File

@@ -31,8 +31,10 @@ import java.util.Set;
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 @@ public final class ActiveServices {
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;
}
}
}