summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityThread.java149
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java25
-rw-r--r--rs/java/android/renderscript/AllocationAdapter.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java3
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java320
-rw-r--r--services/net/java/android/net/dhcp/DhcpAckPacket.java4
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java17
-rw-r--r--services/net/java/android/net/dhcp/DhcpOfferPacket.java4
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacket.java97
-rw-r--r--services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java90
12 files changed, 583 insertions, 259 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9f24de8dfe65..beaf3cf939b6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -101,11 +101,13 @@ import android.view.WindowManagerGlobal;
import android.renderscript.RenderScriptCacheDir;
import android.security.keystore.AndroidKeyStoreProvider;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastPrintWriter;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -163,6 +165,7 @@ public final class ActivityThread {
private static final boolean DEBUG_SERVICE = false;
private static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
+ private static final boolean DEBUG_ORDER = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
@@ -175,6 +178,10 @@ public final class ActivityThread {
/** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
public static final int SERVICE_DONE_EXECUTING_STOP = 2;
+ // Details for pausing activity.
+ private static final int USER_LEAVING = 1;
+ private static final int DONT_REPORT = 2;
+
private ContextImpl mSystemContext;
static IPackageManager sPackageManager;
@@ -231,6 +238,12 @@ public final class ActivityThread {
final ArrayList<ActivityClientRecord> mRelaunchingActivities
= new ArrayList<ActivityClientRecord>();
Configuration mPendingConfiguration = null;
+ // Because we merge activity relaunch operations we can't depend on the ordering provided by
+ // the handler messages. We need to introduce secondary ordering mechanism, which will allow
+ // us to drop certain events, if we know that they happened before relaunch we already executed.
+ // This represents the order of receiving the request from AM.
+ @GuardedBy("mResourcesManager")
+ int mLifecycleSeq = 0;
private final ResourcesManager mResourcesManager;
@@ -319,6 +332,14 @@ public final class ActivityThread {
WindowManager mPendingRemoveWindowManager;
boolean mPreserveWindow;
+ // Set for relaunch requests, indicates the order number of the relaunch operation, so it
+ // can be compared with other lifecycle operations.
+ int relaunchSeq = 0;
+
+ // Can only be accessed from the UI thread. This represents the latest processed message
+ // that is related to lifecycle events/
+ int lastProcessedSeq = 0;
+
ActivityClientRecord() {
parent = null;
embeddedID = null;
@@ -592,18 +613,25 @@ public final class ActivityThread {
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
+ int seq = getLifecycleSeq();
+ if (DEBUG_ORDER) Slog.d(TAG, "pauseActivity " + ActivityThread.this
+ + " operation received seq: " + seq);
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
- (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
- configChanges);
+ (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
+ configChanges,
+ seq);
}
public final void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) {
- sendMessage(
+ int seq = getLifecycleSeq();
+ if (DEBUG_ORDER) Slog.d(TAG, "stopActivity " + ActivityThread.this
+ + " operation received seq: " + seq);
+ sendMessage(
showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
- token, 0, configChanges);
+ token, 0, configChanges, seq);
}
public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
@@ -618,8 +646,11 @@ public final class ActivityThread {
public final void scheduleResumeActivity(IBinder token, int processState,
boolean isForward, Bundle resumeArgs) {
+ int seq = getLifecycleSeq();
+ if (DEBUG_ORDER) Slog.d(TAG, "resumeActivity " + ActivityThread.this
+ + " operation received seq: " + seq);
updateProcessState(processState, false);
- sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
+ sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq);
}
public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
@@ -1245,6 +1276,12 @@ public final class ActivityThread {
}
}
+ private int getLifecycleSeq() {
+ synchronized (mResourcesManager) {
+ return mLifecycleSeq++;
+ }
+ }
+
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
@@ -1373,29 +1410,34 @@ public final class ActivityThread {
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
- case PAUSE_ACTIVITY:
+ case PAUSE_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
- (msg.arg1&2) != 0);
+ SomeArgs args = (SomeArgs) msg.obj;
+ handlePauseActivity((IBinder) args.arg1, false,
+ (args.argi1 & USER_LEAVING) != 0, args.argi2,
+ (args.argi1 & DONT_REPORT) != 0, args.argi3);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case PAUSE_ACTIVITY_FINISHING:
+ } break;
+ case PAUSE_ACTIVITY_FINISHING: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
- (msg.arg1&1) != 0);
+ SomeArgs args = (SomeArgs) msg.obj;
+ handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
+ args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case STOP_ACTIVITY_SHOW:
+ } break;
+ case STOP_ACTIVITY_SHOW: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- handleStopActivity((IBinder)msg.obj, true, msg.arg2);
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case STOP_ACTIVITY_HIDE:
+ } break;
+ case STOP_ACTIVITY_HIDE: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- handleStopActivity((IBinder)msg.obj, false, msg.arg2);
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleStopActivity((IBinder) args.arg1, false, args.argi2, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
+ } break;
case SHOW_WINDOW:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
handleWindowVisibility((IBinder)msg.obj, true);
@@ -1408,7 +1450,9 @@ public final class ActivityThread {
break;
case RESUME_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
- handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
+ args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SEND_RESULT:
@@ -1587,6 +1631,10 @@ public final class ActivityThread {
handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
break;
}
+ Object obj = msg.obj;
+ if (obj instanceof SomeArgs) {
+ ((SomeArgs) obj).recycle();
+ }
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
@@ -2311,6 +2359,21 @@ public final class ActivityThread {
mH.sendMessage(msg);
}
+ private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
+ if (DEBUG_MESSAGES) Slog.v(
+ TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
+ "seq= " + seq);
+ Message msg = Message.obtain();
+ msg.what = what;
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = obj;
+ args.argi1 = arg1;
+ args.argi2 = arg2;
+ args.argi3 = seq;
+ msg.obj = args;
+ mH.sendMessage(msg);
+ }
+
final void scheduleContextCleanup(ContextImpl context, String who,
String what) {
ContextCleanupInfo cci = new ContextCleanupInfo();
@@ -2516,7 +2579,7 @@ public final class ActivityThread {
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
- !r.activity.mFinished && !r.startsNotResumed);
+ !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out
@@ -3216,14 +3279,19 @@ public final class ActivityThread {
}
final void handleResumeActivity(IBinder token,
- boolean clearHide, boolean isForward, boolean reallyResume) {
+ boolean clearHide, boolean isForward, boolean reallyResume, int seq) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
+ return;
+ }
+
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
- ActivityClientRecord r = performResumeActivity(token, clearHide);
+ r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
@@ -3400,8 +3468,11 @@ public final class ActivityThread {
}
private void handlePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport) {
+ boolean userLeaving, int configChanges, boolean dontReport, int seq) {
ActivityClientRecord r = mActivities.get(token);
+ if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
+ return;
+ }
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
if (userLeaving) {
@@ -3639,8 +3710,11 @@ public final class ActivityThread {
}
}
- private void handleStopActivity(IBinder token, boolean show, int configChanges) {
+ private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
ActivityClientRecord r = mActivities.get(token);
+ if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
+ return;
+ }
r.activity.mConfigChangeFlags |= configChanges;
StopInfo info = new StopInfo();
@@ -3669,6 +3743,20 @@ public final class ActivityThread {
mSomeActivitiesChanged = true;
}
+ private static boolean checkAndUpdateLifecycleSeq(int seq, ActivityClientRecord r,
+ String action) {
+ if (r == null) {
+ return true;
+ }
+ if (seq < r.lastProcessedSeq) {
+ if (DEBUG_ORDER) Slog.d(TAG, action + " for " + r + " ignored, because seq=" + seq
+ + " < mCurrentLifecycleSeq=" + r.lastProcessedSeq);
+ return false;
+ }
+ r.lastProcessedSeq = seq;
+ return true;
+ }
+
final void performRestartActivity(IBinder token) {
ActivityClientRecord r = mActivities.get(token);
if (r.stopped) {
@@ -4070,7 +4158,10 @@ public final class ActivityThread {
target.overrideConfig = overrideConfig;
}
target.pendingConfigChanges |= configChanges;
+ target.relaunchSeq = getLifecycleSeq();
}
+ if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this
+ + " operation received seq: " + target.relaunchSeq);
}
private void handleRelaunchActivity(ActivityClientRecord tmp) {
@@ -4115,6 +4206,12 @@ public final class ActivityThread {
}
}
+ if (tmp.lastProcessedSeq > tmp.relaunchSeq) {
+ Slog.wtf(TAG, "For some reason target: " + tmp + " has lower sequence: "
+ + tmp.relaunchSeq + " than current sequence: " + tmp.lastProcessedSeq);
+ } else {
+ tmp.lastProcessedSeq = tmp.relaunchSeq;
+ }
if (tmp.createdConfig != null) {
// If the activity manager is passing us its current config,
// assume that is really what we want regardless of what we
@@ -4148,6 +4245,8 @@ public final class ActivityThread {
r.activity.mConfigChangeFlags |= configChanges;
r.onlyLocalRequest = tmp.onlyLocalRequest;
r.mPreserveWindow = tmp.mPreserveWindow;
+ r.lastProcessedSeq = tmp.lastProcessedSeq;
+ r.relaunchSeq = tmp.relaunchSeq;
Intent currentIntent = r.activity.mIntent;
r.activity.mChangingConfigurations = true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 48a12ed5a025..65981121d9d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,11 +16,13 @@
package com.android.systemui.recents.model;
+import android.animation.ObjectAnimator;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.misc.NamedCounter;
@@ -193,10 +195,37 @@ public class TaskStack {
BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 192,
new RectF(0, 0.7f, 1, 1), new RectF(0, 0.7f, 1, 1), new RectF(0, 0, 1, 0.3f));
+ // Represents the view state of this dock state
+ public class ViewState {
+ public final int dockAreaAlpha;
+ public final ColorDrawable dockAreaOverlay;
+ private ObjectAnimator dockAreaOverlayAnimator;
+
+ private ViewState(int alpha) {
+ dockAreaAlpha = alpha;
+ dockAreaOverlay = new ColorDrawable(0xFFffffff);
+ dockAreaOverlay.setAlpha(0);
+ }
+
+ /**
+ * Creates a new alpha animation.
+ */
+ public void startAlphaAnimation(int alpha, int duration) {
+ if (dockAreaOverlay.getAlpha() != alpha) {
+ if (dockAreaOverlayAnimator != null) {
+ dockAreaOverlayAnimator.cancel();
+ }
+ dockAreaOverlayAnimator = ObjectAnimator.ofInt(dockAreaOverlay, "alpha", alpha);
+ dockAreaOverlayAnimator.setDuration(duration);
+ dockAreaOverlayAnimator.start();
+ }
+ }
+ }
+
public final int createMode;
- public final int dockAreaAlpha;
- private final RectF touchArea;
+ public final ViewState viewState;
private final RectF dockArea;
+ private final RectF touchArea;
private final RectF stackArea;
/**
@@ -207,9 +236,9 @@ public class TaskStack {
DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
RectF stackArea) {
this.createMode = createMode;
- this.dockAreaAlpha = dockAreaAlpha;
- this.touchArea = touchArea;
+ this.viewState = new ViewState(dockAreaAlpha);
this.dockArea = dockArea;
+ this.touchArea = touchArea;
this.stackArea = stackArea;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index f12042178f13..126612074a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,19 +16,17 @@
package com.android.systemui.recents.views;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -38,7 +36,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
-import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -91,8 +88,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
RecentsViewTouchHandler mTouchHandler;
DragView mDragView;
- ColorDrawable mDockRegionOverlay;
- ObjectAnimator mDockRegionOverlayAnimator;
+ TaskStack.DockState[] mVisibleDockStates = {
+ TaskStack.DockState.LEFT,
+ TaskStack.DockState.TOP,
+ TaskStack.DockState.RIGHT,
+ TaskStack.DockState.BOTTOM,
+ };
Interpolator mFastOutSlowInInterpolator;
@@ -118,9 +119,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mTouchHandler = new RecentsViewTouchHandler(this);
- mDockRegionOverlay = new ColorDrawable(0xFFffffff);
- mDockRegionOverlay.setAlpha(0);
- mDockRegionOverlay.setCallback(this);
}
/** Sets the callbacks */
@@ -383,14 +381,23 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- if (mDockRegionOverlay.getAlpha() > 0) {
- mDockRegionOverlay.draw(canvas);
+ for (int i = mVisibleDockStates.length - 1; i >= 0; i--) {
+ Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay;
+ if (d.getAlpha() > 0) {
+ d.draw(canvas);
+ }
}
}
@Override
protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || who == mDockRegionOverlay;
+ for (int i = mVisibleDockStates.length - 1; i >= 0; i--) {
+ Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay;
+ if (d == who) {
+ return true;
+ }
+ }
+ return super.verifyDrawable(who);
}
/** Notifies each task view of the user interaction. */
@@ -775,11 +782,17 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mDragView = event.dragView;
addView(mDragView);
- updateDockRegion(TaskStack.DockState.NONE);
+ updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
+ TaskStack.DockState.NONE.viewState.dockAreaAlpha);
}
public final void onBusEvent(DragDockStateChangedEvent event) {
- updateDockRegion(event.dockState);
+ if (event.dockState == TaskStack.DockState.NONE) {
+ updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
+ TaskStack.DockState.NONE.viewState.dockAreaAlpha);
+ } else {
+ updateVisibleDockRegions(new TaskStack.DockState[] {event.dockState}, -1);
+ }
}
public final void onBusEvent(final DragEndEvent event) {
@@ -790,8 +803,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// Remove the drag view
removeView(mDragView);
mDragView = null;
- mDockRegionOverlay.setAlpha(0);
- invalidate();
+ updateVisibleDockRegions(null, -1);
// Dock the new task if we are hovering over a valid dock state
if (event.dockState != TaskStack.DockState.NONE) {
@@ -818,7 +830,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
.start();
// Animate the overlay alpha back to 0
- updateDockRegionAlpha(0);
+ updateVisibleDockRegions(null, -1);
} else {
event.postAnimationTrigger.decrement();
}
@@ -827,24 +839,26 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/**
* Updates the dock region to match the specified dock state.
*/
- private void updateDockRegion(TaskStack.DockState dockState) {
- TaskStack.DockState boundsDockState = dockState;
- if (dockState == TaskStack.DockState.NONE) {
- // If the dock state is null, then use the bounds of the preferred dock state for this
- // orientation
- boundsDockState = mTouchHandler.getPreferredDockStateForCurrentOrientation();
+ private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, int overrideAlpha) {
+ ArraySet<TaskStack.DockState> newDockStatesSet = new ArraySet<>();
+ if (newDockStates != null) {
+ for (TaskStack.DockState dockState : newDockStates) {
+ newDockStatesSet.add(dockState);
+ }
}
- mDockRegionOverlay.setBounds(
- boundsDockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
- updateDockRegionAlpha(dockState.dockAreaAlpha);
- }
-
- private void updateDockRegionAlpha(int alpha) {
- if (mDockRegionOverlayAnimator != null) {
- mDockRegionOverlayAnimator.cancel();
+ for (TaskStack.DockState dockState : mVisibleDockStates) {
+ TaskStack.DockState.ViewState viewState = dockState.viewState;
+ if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
+ // This is no longer visible, so hide it
+ viewState.startAlphaAnimation(0, 150);
+ } else {
+ // This state is now visible, update the bounds and show it
+ int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha);
+ viewState.dockAreaOverlay.setBounds(
+ dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
+ viewState.dockAreaOverlay.setCallback(this);
+ viewState.startAlphaAnimation(alpha, 150);
+ }
}
- mDockRegionOverlayAnimator = ObjectAnimator.ofInt(mDockRegionOverlay, "alpha", alpha);
- mDockRegionOverlayAnimator.setDuration(150);
- mDockRegionOverlayAnimator.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 31ac755007f7..76399f4446ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -19,6 +19,7 @@ package com.android.systemui.recents.views;
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
+import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
@@ -32,13 +33,17 @@ import com.android.systemui.recents.model.TaskStack;
* Represents the dock regions for each orientation.
*/
class DockRegion {
- public static TaskStack.DockState[] LANDSCAPE = {
+ public static TaskStack.DockState[] PHONE_LANDSCAPE = {
TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
};
- public static TaskStack.DockState[] PORTRAIT = {
+ public static TaskStack.DockState[] PHONE_PORTRAIT = {
// We only allow docking to the top for now
TaskStack.DockState.TOP
};
+ public static TaskStack.DockState[] TABLET_LANDSCAPE = {
+ TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
+ };
+ public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
}
/**
@@ -60,12 +65,17 @@ class RecentsViewTouchHandler {
mRv = rv;
}
- public TaskStack.DockState getPreferredDockStateForCurrentOrientation() {
+ /**
+ * Returns the preferred dock states for the current orientation.
+ */
+ public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
TaskStack.DockState[] dockStates = isLandscape ?
- DockRegion.LANDSCAPE : DockRegion.PORTRAIT;
- return dockStates[0];
+ (config.isLargeScreen ? DockRegion.TABLET_LANDSCAPE : DockRegion.PHONE_LANDSCAPE) :
+ (config.isLargeScreen ? DockRegion.TABLET_PORTRAIT : DockRegion.PHONE_PORTRAIT);
+ return dockStates;
}
/** Touch preprocessing for handling below */
@@ -125,10 +135,9 @@ class RecentsViewTouchHandler {
float y = evY - mDragView.getTopLeftOffset().y;
// Update the dock state
- TaskStack.DockState[] dockStates = isLandscape ?
- DockRegion.LANDSCAPE : DockRegion.PORTRAIT;
+ TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
TaskStack.DockState foundDockState = TaskStack.DockState.NONE;
- for (int i = 0; i < dockStates.length; i++) {
+ for (int i = dockStates.length - 1; i >= 0; i--) {
TaskStack.DockState state = dockStates[i];
if (state.touchAreaContainsPoint(width, height, evX, evY)) {
foundDockState = state;
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index 9bfd6ec48d4e..6d7e97ebb0fe 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -244,23 +244,23 @@ public class AllocationAdapter extends Allocation {
/**
*
*
- * Create an arbitrary window into the base allocation
+ * Create an arbitrary window into the base allocation.
* The type describes the shape of the window.
*
* Any dimensions present in the type must be equal or smaller
* to the dimensions in the source allocation. A dimension
* present in the allocation that is not present in the type
- * will be constrained away with the selectors
+ * will be constrained away with the selectors.
*
- * If a dimension is present in the type and allcation one of
- * two things will happen
+ * If a dimension is present in both the type and allocation, one of
+ * two things will happen.
*
- * If the type is smaller than the allocation a window will be
+ * If the type is smaller than the allocation, a window will be
* created, the selected value in the adapter for that dimension
- * will act as the base address and the type will describe the
+ * will act as the base address, and the type will describe the
* size of the view starting at that point.
*
- * If the type and allocation dimension are of the same size
+ * If the type and allocation dimension are of the same size,
* then setting the selector for the dimension will be an error.
*/
static public AllocationAdapter createTyped(RenderScript rs, Allocation a, Type t) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8bec7f7cad97..e4650094be31 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -535,7 +535,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
ActivityRecord r = getHomeActivity();
- if (r != null) {
+ // Only resume home activity if isn't finishing.
+ if (r != null && !r.finishing) {
mService.setFocusedActivityLocked(r, reason);
return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 8f24b462f607..a54a61a1c68a 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -90,6 +90,10 @@ abstract public class ManagedServices {
= new ArraySet<ComponentName>();
// Just the packages from mEnabledServicesForCurrentProfiles
private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>();
+ // List of packages in restored setting across all mUserProfiles, for quick
+ // filtering upon package updates.
+ private ArraySet<String> mRestoredPackages = new ArraySet<>();
+
// Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
// user change).
@@ -108,6 +112,7 @@ abstract public class ManagedServices {
mRestoreReceiver = new SettingRestoredReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
context.registerReceiver(mRestoreReceiver, filter);
+ rebuildRestoredPackages();
}
class SettingRestoredReceiver extends BroadcastReceiver {
@@ -166,7 +171,7 @@ abstract public class ManagedServices {
// By convention, restored settings are replicated to another settings
// entry, named similarly but with a disambiguation suffix.
- public static final String restoredSettingName(Config config) {
+ public static String restoredSettingName(Config config) {
return config.secureSettingName + ":restored";
}
@@ -184,7 +189,8 @@ abstract public class ManagedServices {
restoredSettingName(mConfig),
newValue,
userid);
- disableNonexistentServices(userid);
+ updateSettingsAccordingToInstalledServices(userid);
+ rebuildRestoredPackages();
}
}
}
@@ -198,9 +204,11 @@ abstract public class ManagedServices {
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
boolean anyServicesInvolved = false;
+
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
- if (mEnabledServicesPackageNames.contains(pkgName)) {
+ if (mEnabledServicesPackageNames.contains(pkgName) ||
+ mRestoredPackages.contains(pkgName)) {
anyServicesInvolved = true;
}
}
@@ -209,7 +217,8 @@ abstract public class ManagedServices {
if (anyServicesInvolved) {
// if we're not replacing a package, clean up orphaned bits
if (!queryReplace) {
- disableNonexistentServices();
+ updateSettingsAccordingToInstalledServices();
+ rebuildRestoredPackages();
}
// make sure we're still bound to any of our services who may have just upgraded
rebindServices();
@@ -218,6 +227,7 @@ abstract public class ManagedServices {
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
+ rebuildRestoredPackages();
if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
return;
@@ -229,7 +239,7 @@ abstract public class ManagedServices {
checkNotNull(service);
final IBinder token = service.asBinder();
final int N = mServices.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
final ManagedServiceInfo info = mServices.get(i);
if (info.service.asBinder() == token) return info;
}
@@ -252,92 +262,142 @@ abstract public class ManagedServices {
}
}
+
+ private void rebuildRestoredPackages() {
+ mRestoredPackages.clear();
+ String settingName = restoredSettingName(mConfig);
+ int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final int N = userIds.length;
+ for (int i = 0; i < N; ++i) {
+ ArraySet<ComponentName> names = loadComponentNamesFromSetting(settingName, userIds[i]);
+ if (names == null)
+ continue;
+ for (ComponentName name: names) {
+ mRestoredPackages.add(name.getPackageName());
+ }
+ }
+ }
+
+
+ private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, int userId) {
+ final ContentResolver cr = mContext.getContentResolver();
+ String settingValue = Settings.Secure.getStringForUser(
+ cr,
+ settingName,
+ userId);
+ if (TextUtils.isEmpty(settingValue))
+ return null;
+ String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
+ ArraySet<ComponentName> result = new ArraySet<>(restored.length);
+ for (int i = 0; i < restored.length; i++) {
+ ComponentName value = ComponentName.unflattenFromString(restored[i]);
+ if (null != value) {
+ result.add(value);
+ }
+ }
+ return result;
+ }
+
+ private void storeComponentsToSetting(Set<ComponentName> components,
+ String settingName,
+ int userId) {
+ String[] componentNames = null;
+ if (null != components) {
+ componentNames = new String[components.size()];
+ int index = 0;
+ for (ComponentName c: components) {
+ componentNames[index++] = c.flattenToString();
+ }
+ }
+ final String value = (componentNames == null) ? "" :
+ TextUtils.join(ENABLED_SERVICES_SEPARATOR, componentNames);
+ final ContentResolver cr = mContext.getContentResolver();
+ Settings.Secure.putStringForUser(
+ cr,
+ settingName,
+ value,
+ userId);
+ }
+
+
/**
* Remove access for any services that no longer exist.
*/
- private void disableNonexistentServices() {
+ private void updateSettingsAccordingToInstalledServices() {
int[] userIds = mUserProfiles.getCurrentProfileIds();
final int N = userIds.length;
- for (int i = 0 ; i < N; ++i) {
- disableNonexistentServices(userIds[i]);
+ for (int i = 0; i < N; ++i) {
+ updateSettingsAccordingToInstalledServices(userIds[i]);
}
+ rebuildRestoredPackages();
}
- private void disableNonexistentServices(int userId) {
- final ContentResolver cr = mContext.getContentResolver();
+ private void updateSettingsAccordingToInstalledServices(int userId) {
boolean restoredChanged = false;
- if (mRestored == null) {
- String restoredSetting = Settings.Secure.getStringForUser(
- cr,
- restoredSettingName(mConfig),
- userId);
- if (!TextUtils.isEmpty(restoredSetting)) {
- if (DEBUG) Slog.d(TAG, "restored: " + restoredSetting);
- String[] restored = restoredSetting.split(ENABLED_SERVICES_SEPARATOR);
- mRestored = new ArraySet<String>(Arrays.asList(restored));
- } else {
- mRestored = new ArraySet<String>();
+ boolean currentChanged = false;
+ Set<ComponentName> restored =
+ loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
+ Set<ComponentName> current =
+ loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
+ Set<ComponentName> installed = new ArraySet<>();
+
+ final PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+ new Intent(mConfig.serviceInterface),
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ userId);
+ if (DEBUG)
+ Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedServices.get(i);
+ ServiceInfo info = resolveInfo.serviceInfo;
+
+ ComponentName component = new ComponentName(info.packageName, info.name);
+ if (!mConfig.bindPermission.equals(info.permission)) {
+ Slog.w(TAG, "Skipping " + getCaption() + " service "
+ + info.packageName + "/" + info.name
+ + ": it does not require the permission "
+ + mConfig.bindPermission);
+ continue;
}
+ installed.add(component);
}
- String flatIn = Settings.Secure.getStringForUser(
- cr,
- mConfig.secureSettingName,
- userId);
- if (!TextUtils.isEmpty(flatIn)) {
- if (DEBUG) Slog.v(TAG, "flat before: " + flatIn);
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
- new Intent(mConfig.serviceInterface),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- userId);
- if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
- Set<ComponentName> installed = new ArraySet<ComponentName>();
- for (int i = 0, count = installedServices.size(); i < count; i++) {
- ResolveInfo resolveInfo = installedServices.get(i);
- ServiceInfo info = resolveInfo.serviceInfo;
-
- ComponentName component = new ComponentName(info.packageName, info.name);
- if (!mConfig.bindPermission.equals(info.permission)) {
- Slog.w(TAG, "Skipping " + getCaption() + " service "
- + info.packageName + "/" + info.name
- + ": it does not require the permission "
- + mConfig.bindPermission);
- restoredChanged |= mRestored.remove(component.flattenToString());
+
+ ArraySet<ComponentName> retained = new ArraySet<>();
+
+ for (ComponentName component : installed) {
+ if (null != restored) {
+ boolean wasRestored = restored.remove(component);
+ if (wasRestored) {
+ // Freshly installed package has service that was mentioned in restored setting.
+ if (DEBUG)
+ Slog.v(TAG, "Restoring " + component + " for user " + userId);
+ restoredChanged = true;
+ currentChanged = true;
+ retained.add(component);
continue;
}
- installed.add(component);
}
- String flatOut = "";
- if (!installed.isEmpty()) {
- String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR);
- ArrayList<String> remaining = new ArrayList<String>(enabled.length);
- for (int i = 0; i < enabled.length; i++) {
- ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
- if (installed.contains(enabledComponent)) {
- remaining.add(enabled[i]);
- restoredChanged |= mRestored.remove(enabled[i]);
- }
- }
- remaining.addAll(mRestored);
- flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining);
- }
- if (DEBUG) Slog.v(TAG, "flat after: " + flatOut);
- if (!flatIn.equals(flatOut)) {
- Settings.Secure.putStringForUser(cr,
- mConfig.secureSettingName,
- flatOut, userId);
- }
- if (restoredChanged) {
- if (DEBUG) Slog.d(TAG, "restored changed; rewriting");
- final String flatRestored = TextUtils.join(ENABLED_SERVICES_SEPARATOR,
- mRestored.toArray());
- Settings.Secure.putStringForUser(cr,
- restoredSettingName(mConfig),
- flatRestored,
- userId);
+ if (null != current) {
+ if (current.contains(component))
+ retained.add(component);
}
}
+
+ currentChanged |= ((current == null ? 0 : current.size()) != retained.size());
+
+ if (currentChanged) {
+ if (DEBUG) Slog.v(TAG, "List of " + getCaption() + " services was updated " + current);
+ storeComponentsToSetting(retained, mConfig.secureSettingName, userId);
+ }
+
+ if (restoredChanged) {
+ if (DEBUG) Slog.v(TAG,
+ "List of " + getCaption() + " restored services was updated " + restored);
+ storeComponentsToSetting(restored, restoredSettingName(mConfig), userId);
+ }
}
/**
@@ -349,18 +409,15 @@ abstract public class ManagedServices {
final int[] userIds = mUserProfiles.getCurrentProfileIds();
final int nUserIds = userIds.length;
- final SparseArray<String> flat = new SparseArray<String>();
+ final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
for (int i = 0; i < nUserIds; ++i) {
- flat.put(userIds[i], Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- mConfig.secureSettingName,
- userIds[i]));
+ componentsByUser.put(userIds[i],
+ loadComponentNamesFromSetting(mConfig.secureSettingName, userIds[i]));
}
- ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
- final SparseArray<ArrayList<ComponentName>> toAdd
- = new SparseArray<ArrayList<ComponentName>>();
+ final ArrayList<ManagedServiceInfo> toRemove = new ArrayList<>();
+ final SparseArray<ArrayList<ComponentName>> toAdd = new SparseArray<>();
synchronized (mMutex) {
// Unbind automatically bound services, retain system services.
@@ -370,27 +427,25 @@ abstract public class ManagedServices {
}
}
- final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
- final ArraySet<String> newPackages = new ArraySet<String>();
+ final ArraySet<ComponentName> newEnabled = new ArraySet<>();
+ final ArraySet<String> newPackages = new ArraySet<>();
for (int i = 0; i < nUserIds; ++i) {
- final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
+ // decode the list of components
+ final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
+ if (null == userComponents) {
+ toAdd.put(userIds[i], new ArrayList<ComponentName>());
+ continue;
+ }
+
+ final ArrayList<ComponentName> add = new ArrayList<>(userComponents);
toAdd.put(userIds[i], add);
- // decode the list of components
- String toDecode = flat.get(userIds[i]);
- if (toDecode != null) {
- String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR);
- for (int j = 0; j < components.length; j++) {
- final ComponentName component
- = ComponentName.unflattenFromString(components[j]);
- if (component != null) {
- newEnabled.add(component);
- add.add(component);
- newPackages.add(component.getPackageName());
- }
- }
+ newEnabled.addAll(userComponents);
+ for (int j = 0; j < userComponents.size(); j++) {
+ final ComponentName component = userComponents.valueAt(i);
+ newPackages.add(component.getPackageName());
}
}
mEnabledServicesForCurrentProfiles = newEnabled;
@@ -416,7 +471,7 @@ abstract public class ManagedServices {
}
}
- mLastSeenProfileIds = mUserProfiles.getCurrentProfileIds();
+ mLastSeenProfileIds = userIds;
}
/**
@@ -434,7 +489,7 @@ abstract public class ManagedServices {
mServicesBinding.add(servicesBindingTag);
final int N = mServices.size();
- for (int i=N-1; i>=0; i--) {
+ for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
if (name.equals(info.component)
&& info.userid == userid) {
@@ -469,39 +524,39 @@ abstract public class ManagedServices {
try {
if (DEBUG) Slog.v(TAG, "binding: " + intent);
- if (!mContext.bindServiceAsUser(intent,
- new ServiceConnection() {
- IInterface mService;
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- boolean added = false;
- ManagedServiceInfo info = null;
- synchronized (mMutex) {
- mServicesBinding.remove(servicesBindingTag);
- try {
- mService = asInterface(binder);
- info = newServiceInfo(mService, name,
- userid, false /*isSystem*/, this, targetSdkVersion);
- binder.linkToDeath(info, 0);
- added = mServices.add(info);
- } catch (RemoteException e) {
- // already dead
- }
- }
- if (added) {
- onServiceAdded(info);
- }
+ ServiceConnection serviceConnection = new ServiceConnection() {
+ IInterface mService;
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ boolean added = false;
+ ManagedServiceInfo info = null;
+ synchronized (mMutex) {
+ mServicesBinding.remove(servicesBindingTag);
+ try {
+ mService = asInterface(binder);
+ info = newServiceInfo(mService, name,
+ userid, false /*isSystem*/, this, targetSdkVersion);
+ binder.linkToDeath(info, 0);
+ added = mServices.add(info);
+ } catch (RemoteException e) {
+ // already dead
}
+ }
+ if (added) {
+ onServiceAdded(info);
+ }
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Slog.v(TAG, getCaption() + " connection lost: " + name);
- }
- },
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.v(TAG, getCaption() + " connection lost: " + name);
+ }
+ };
+ if (!mContext.bindServiceAsUser(intent,
+ serviceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(userid)))
- {
+ new UserHandle(userid))) {
mServicesBinding.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
return;
@@ -519,7 +574,7 @@ abstract public class ManagedServices {
private void unregisterService(ComponentName name, int userid) {
synchronized (mMutex) {
final int N = mServices.size();
- for (int i=N-1; i>=0; i--) {
+ for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
if (name.equals(info.component)
&& info.userid == userid) {
@@ -548,7 +603,7 @@ abstract public class ManagedServices {
ManagedServiceInfo serviceInfo = null;
synchronized (mMutex) {
final int N = mServices.size();
- for (int i=N-1; i>=0; i--) {
+ for (int i = N - 1; i >= 0; i--) {
final ManagedServiceInfo info = mServices.get(i);
if (info.service.asBinder() == service.asBinder()
&& info.userid == userid) {
@@ -622,6 +677,7 @@ abstract public class ManagedServices {
if (DEBUG) Slog.d(TAG, "Setting changed: mSecureSettingsUri=" + mSecureSettingsUri +
" / uri=" + uri);
rebindServices();
+ rebuildRestoredPackages();
}
}
}
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index 334f708b88a9..df44b11fffa5 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -46,7 +46,7 @@ class DhcpAckPacket extends DhcpPacket {
return s + " ACK: your new IP " + mYourIp +
", netmask " + mSubnetMask +
- ", gateway " + mGateway + dnsServers +
+ ", gateways " + mGateways + dnsServers +
", lease time " + mLeaseTime;
}
@@ -79,7 +79,7 @@ class DhcpAckPacket extends DhcpPacket {
}
addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buffer, DHCP_ROUTER, mGateway);
+ addTlv(buffer, DHCP_ROUTER, mGateways);
addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 1a728a0ca261..28cb114ccf6b 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -356,21 +356,22 @@ public class DhcpClient extends BaseDhcpStateMachine {
public void run() {
maybeLog("Receive thread started");
while (!stopped) {
+ int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
try {
- int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
+ length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
DhcpPacket packet = null;
packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- if (packet != null) {
- maybeLog("Received packet: " + packet);
- sendMessage(CMD_RECEIVED_PACKET, packet);
- } else if (PACKET_DBG) {
- Log.d(TAG,
- "Can't parse packet" + HexDump.dumpHexString(mPacket, 0, length));
- }
+ maybeLog("Received packet: " + packet);
+ sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (IOException|ErrnoException e) {
if (!stopped) {
Log.e(TAG, "Read error", e);
}
+ } catch (DhcpPacket.ParseException e) {
+ Log.e(TAG, "Can't parse packet: " + e.getMessage());
+ if (PACKET_DBG) {
+ Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
+ }
}
}
maybeLog("Receive thread stopped");
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index 7ca7100bbabc..99154ef0498c 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -48,7 +48,7 @@ class DhcpOfferPacket extends DhcpPacket {
}
return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateway " + mGateway +
+ dnsServers + ", gateways " + mGateways +
" lease time " + mLeaseTime + ", domain " + mDomainName;
}
@@ -81,7 +81,7 @@ class DhcpOfferPacket extends DhcpPacket {
}
addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buffer, DHCP_ROUTER, mGateway);
+ addTlv(buffer, DHCP_ROUTER, mGateways);
addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index cbf8fc21b809..8927bfa25654 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -114,6 +114,11 @@ abstract class DhcpPacket {
protected static final int MAX_LENGTH = 1500;
/**
+ * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
+ */
+ private static final int DHCP_MAGIC_COOKIE = 0x63825363;
+
+ /**
* DHCP Optional Type: DHCP Subnet Mask
*/
protected static final byte DHCP_SUBNET_MASK = 1;
@@ -123,7 +128,7 @@ abstract class DhcpPacket {
* DHCP Optional Type: DHCP Router
*/
protected static final byte DHCP_ROUTER = 3;
- protected Inet4Address mGateway;
+ protected List <Inet4Address> mGateways;
/**
* DHCP Optional Type: DHCP DNS Server
@@ -403,7 +408,7 @@ abstract class DhcpPacket {
(HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
+ 64 // empty server host name (64 bytes)
+ 128); // empty boot file name (128 bytes)
- buf.putInt(0x63825363); // magic number
+ buf.putInt(DHCP_MAGIC_COOKIE); // magic number
finishPacket(buf);
// round up to an even number of octets
@@ -668,6 +673,20 @@ abstract class DhcpPacket {
return new String(bytes, 0, length, StandardCharsets.US_ASCII);
}
+ private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
+ return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
+ }
+
+ private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
+ return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
+ }
+
+ public static class ParseException extends Exception {
+ public ParseException(String msg, Object... args) {
+ super(String.format(msg, args));
+ }
+ }
+
/**
* Creates a concrete DhcpPacket from the supplied ByteBuffer. The
* buffer may have an L2 encapsulation (which is the full EthernetII
@@ -677,7 +696,7 @@ abstract class DhcpPacket {
* A subset of the optional parameters are parsed and are stored
* in object fields.
*/
- public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
+ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
{
// bootp parameters
int transactionId;
@@ -687,8 +706,8 @@ abstract class DhcpPacket {
Inet4Address nextIp;
Inet4Address relayIp;
byte[] clientMac;
- List<Inet4Address> dnsServers = new ArrayList<Inet4Address>();
- Inet4Address gateway = null; // aka router
+ List<Inet4Address> dnsServers = new ArrayList<>();
+ List<Inet4Address> gateways = new ArrayList<>(); // aka router
Inet4Address serverIdentifier = null;
Inet4Address netMask = null;
String message = null;
@@ -720,7 +739,8 @@ abstract class DhcpPacket {
// check to see if we need to parse L2, IP, and UDP encaps
if (pktType == ENCAP_L2) {
if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
- return null;
+ throw new ParseException("L2 packet too short, %d < %d",
+ packet.remaining(), MIN_PACKET_LENGTH_L2);
}
byte[] l2dst = new byte[6];
@@ -732,18 +752,20 @@ abstract class DhcpPacket {
short l2type = packet.getShort();
if (l2type != OsConstants.ETH_P_IP)
- return null;
+ throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x",
+ l2type, OsConstants.ETH_P_IP);
}
if (pktType <= ENCAP_L3) {
if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
- return null;
+ throw new ParseException("L3 packet too short, %d < %d",
+ packet.remaining(), MIN_PACKET_LENGTH_L3);
}
byte ipTypeAndLength = packet.get();
int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
if (ipVersion != 4) {
- return null;
+ throw new ParseException("Invalid IP version %d", ipVersion);
}
// System.out.println("ipType is " + ipType);
@@ -759,8 +781,9 @@ abstract class DhcpPacket {
ipSrc = readIpAddress(packet);
ipDst = readIpAddress(packet);
- if (ipProto != IP_TYPE_UDP) // UDP
- return null;
+ if (ipProto != IP_TYPE_UDP) {
+ throw new ParseException("Protocol not UDP: %d", ipProto);
+ }
// Skip options. This cannot cause us to read beyond the end of the buffer because the
// IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
@@ -776,13 +799,19 @@ abstract class DhcpPacket {
short udpLen = packet.getShort();
short udpChkSum = packet.getShort();
- if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
+ // Only accept packets to or from the well-known client port (expressly permitting
+ // packets from ports other than the well-known server port; http://b/24687559), and
+ // server-to-server packets, e.g. for relays.
+ if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
+ !isPacketServerToServer(udpSrcPort, udpDstPort)) {
return null;
+ }
}
// We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
- return null;
+ throw new ParseException("Invalid type or BOOTP packet too short, %d < %d",
+ packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
}
byte type = packet.get();
@@ -805,7 +834,7 @@ abstract class DhcpPacket {
packet.get(ipv4addr);
relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
} catch (UnknownHostException ex) {
- return null;
+ throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
}
// Some DHCP servers have been known to announce invalid client hardware address values such
@@ -828,8 +857,10 @@ abstract class DhcpPacket {
int dhcpMagicCookie = packet.getInt();
- if (dhcpMagicCookie != 0x63825363)
- return null;
+ if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
+ throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie,
+ DHCP_MAGIC_COOKIE);
+ }
// parse options
boolean notFinishedOptions = true;
@@ -852,8 +883,9 @@ abstract class DhcpPacket {
expectedLen = 4;
break;
case DHCP_ROUTER:
- gateway = readIpAddress(packet);
- expectedLen = 4;
+ for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
+ gateways.add(readIpAddress(packet));
+ }
break;
case DHCP_DNS_SERVER:
for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
@@ -937,18 +969,20 @@ abstract class DhcpPacket {
}
if (expectedLen != optionLen) {
- return null;
+ throw new ParseException("Invalid length %d for option %d, expected %d",
+ optionLen, optionType, expectedLen);
}
}
} catch (BufferUnderflowException e) {
- return null;
+ throw new ParseException("BufferUnderflowException");
}
}
DhcpPacket newPacket;
switch(dhcpType) {
- case -1: return null;
+ case (byte) 0xFF:
+ throw new ParseException("No DHCP message type option");
case DHCP_MESSAGE_TYPE_DISCOVER:
newPacket = new DhcpDiscoverPacket(
transactionId, secs, clientMac, broadcast);
@@ -981,14 +1015,13 @@ abstract class DhcpPacket {
clientMac);
break;
default:
- System.out.println("Unimplemented type: " + dhcpType);
- return null;
+ throw new ParseException("Unimplemented DHCP type %d", dhcpType);
}
newPacket.mBroadcastAddress = bcAddr;
newPacket.mDnsServers = dnsServers;
newPacket.mDomainName = domainName;
- newPacket.mGateway = gateway;
+ newPacket.mGateways = gateways;
newPacket.mHostName = hostName;
newPacket.mLeaseTime = leaseTime;
newPacket.mMessage = message;
@@ -1009,7 +1042,7 @@ abstract class DhcpPacket {
* Parse a packet from an array of bytes, stopping at the given length.
*/
public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
- {
+ throws ParseException {
ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
return decodeFullPacket(buffer, pktType);
}
@@ -1044,7 +1077,11 @@ abstract class DhcpPacket {
} catch (IllegalArgumentException e) {
return null;
}
- results.gateway = mGateway;
+
+ if (mGateways.size() > 0) {
+ results.gateway = mGateways.get(0);
+ }
+
results.dnsServers.addAll(mDnsServers);
results.domains = mDomainName;
results.serverAddress = mServerIdentifier;
@@ -1086,11 +1123,11 @@ abstract class DhcpPacket {
public static ByteBuffer buildOfferPacket(int encap, int transactionId,
boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
- Inet4Address gateway, List<Inet4Address> dnsServers,
+ List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
- pkt.mGateway = gateway;
+ pkt.mGateways = gateways;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
@@ -1106,11 +1143,11 @@ abstract class DhcpPacket {
public static ByteBuffer buildAckPacket(int encap, int transactionId,
boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
- Inet4Address gateway, List<Inet4Address> dnsServers,
+ List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
- pkt.mGateway = gateway;
+ pkt.mGateways = gateways;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
pkt.mDomainName = domainName;
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index cd3b8bb7e0b9..7e60bf17d96e 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -117,7 +117,7 @@ public class DhcpPacketTest extends TestCase {
private void assertDomainAndVendorInfoParses(
String expectedDomain, byte[] domainBytes,
- String expectedVendorInfo, byte[] vendorInfoBytes) {
+ String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
.setDomainBytes(domainBytes)
.setVendorInfoBytes(vendorInfoBytes)
@@ -158,17 +158,25 @@ public class DhcpPacketTest extends TestCase {
}
private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
- long leaseTimeMillis, byte[] leaseTimeBytes) {
+ long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
if (leaseTimeBytes != null) {
testPacket.setLeaseTimeBytes(leaseTimeBytes);
}
ByteBuffer packet = testPacket.build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpPacket offerPacket = null;
+
if (!expectValid) {
- assertNull(offerPacket);
+ try {
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ fail("Invalid packet parsed successfully: " + offerPacket);
+ } catch (ParseException expected) {
+ }
return;
}
+
+ offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ assertNotNull(offerPacket);
assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
@@ -200,14 +208,14 @@ public class DhcpPacketTest extends TestCase {
}
private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) {
+ byte[] netmaskBytes) throws Exception {
checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
}
private void checkIpAddress(String expected, byte type,
Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) {
+ byte[] netmaskBytes) throws Exception {
ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
.setNetmaskBytes(netmaskBytes)
.build();
@@ -506,4 +514,74 @@ public class DhcpPacketTest extends TestCase {
assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
"lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults);
}
+
+ @SmallTest
+ public void testUdpServerAnySourcePort() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ // Ethernet header.
+ "9cd917000000001c2e0000000800" +
+ // IP header.
+ "45a00148000040003d115087d18194fb0a0f7af2" +
+ // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+ // NOTE: The server source port is not the canonical port 67.
+ "C29F004401341268" +
+ // BOOTP header.
+ "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
+ // MAC address.
+ "9cd91700000000000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options.
+ "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
+ "d18180060f0777766d2e6564751c040a0fffffff000000"
+ ).toCharArray(), false));
+
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ assertTrue(offerPacket instanceof DhcpOfferPacket);
+ assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
+ DhcpResults dhcpResults = offerPacket.toDhcpResults();
+ assertDhcpResults("10.15.122.242/16", "10.15.200.23",
+ "209.129.128.3,209.129.148.3,209.129.128.6",
+ "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults);
+ }
+
+ @SmallTest
+ public void testMultipleRouters() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ // Ethernet header.
+ "fc3d93000000" + "081735000000" + "0800" +
+ // IP header.
+ "45000148c2370000ff117ac2c0a8bd02ffffffff" +
+ // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+ "0043004401343beb" +
+ // BOOTP header.
+ "0201060027f518e20000800000000000c0a8bd310000000000000000" +
+ // MAC address.
+ "fc3d9300000000000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options.
+ "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
+ "0308c0a8bd01ffffff0006080808080808080404ff000000000000"
+ ).toCharArray(), false));
+
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ assertTrue(offerPacket instanceof DhcpOfferPacket);
+ assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
+ DhcpResults dhcpResults = offerPacket.toDhcpResults();
+ assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
+ null, "192.171.189.2", null, 28800, false, dhcpResults);
+ }
}