Merge "Fixed application info null pointer exception."
diff --git a/Android.bp b/Android.bp
index 93c94e3..f6d49e9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -872,7 +872,10 @@
local_include_dir: "core/java",
srcs: [
"core/java/android/net/INetworkStackConnector.aidl",
+ "core/java/android/net/INetworkStackStatusCallback.aidl",
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
+ "core/java/android/net/dhcp/IDhcpServer.aidl",
+ "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
],
api_dir: "aidl/networkstack",
}
diff --git a/api/current.txt b/api/current.txt
index 99d4b95..f0b3310 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33707,6 +33707,7 @@
method public static final void flushPendingCommands();
method public static final int getCallingPid();
method public static final int getCallingUid();
+ method public static final int getCallingUidOrThrow();
method public static final android.os.UserHandle getCallingUserHandle();
method public java.lang.String getInterfaceDescriptor();
method public boolean isBinderAlive();
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index b905e94..0c861cf 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -44,6 +44,9 @@
private static final String INVALID_DISPLAY_ARGUMENTS =
"Error: Invalid arguments for display ID.";
+ private static final float DEFAULT_PRESSURE = 1.0f;
+ private static final float NO_PRESSURE = 0.0f;
+
private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
put("keyboard", InputDevice.SOURCE_KEYBOARD);
put("dpad", InputDevice.SOURCE_DPAD);
@@ -76,6 +79,7 @@
COMMANDS.put("draganddrop", new InputDragAndDrop());
COMMANDS.put("press", new InputPress());
COMMANDS.put("roll", new InputRoll());
+ COMMANDS.put("motionevent", new InputMotionEvent());
}
@Override
@@ -304,6 +308,41 @@
}
}
+ class InputMotionEvent implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendMotionEvent(inputSource, nextArgRequired(), Float.parseFloat(nextArgRequired()),
+ Float.parseFloat(nextArgRequired()), displayId);
+ }
+
+ private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
+ int displayId) {
+ final int action;
+ final float pressure;
+
+ switch (motionEventType.toUpperCase()) {
+ case "DOWN":
+ action = MotionEvent.ACTION_DOWN;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ case "UP":
+ action = MotionEvent.ACTION_UP;
+ pressure = NO_PRESSURE;
+ break;
+ case "MOVE":
+ action = MotionEvent.ACTION_MOVE;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
+ }
+ }
+
/**
* Abstract class for command
* use nextArgRequired or nextArg to check next argument if necessary.
@@ -391,5 +430,6 @@
+ " (Default: touchscreen)");
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
+ out.println(" event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
}
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 23e4ec0..436b4a1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2485,6 +2485,8 @@
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
/** {@hide} */
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
+ /** {@hide} */
+ public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
/**
* Get a more detailed error code after a Tethering or Untethering
diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl
index 29f8828..be0dc07 100644
--- a/core/java/android/net/INetworkStackConnector.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -15,7 +15,11 @@
*/
package android.net;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+
/** @hide */
oneway interface INetworkStackConnector {
- // TODO: requestDhcpServer(), etc. will go here
+ void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
+ in IDhcpServerCallbacks cb);
}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkStackStatusCallback.aidl b/core/java/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..51032d8
--- /dev/null
+++ b/core/java/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.net;
+
+/** @hide */
+oneway interface INetworkStackStatusCallback {
+ void onStatusAvailable(int statusCode);
+}
\ No newline at end of file
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 82a4e31..d4a0ec63 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -25,9 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
@@ -58,6 +61,22 @@
public NetworkStack() { }
+ /**
+ * Create a DHCP server according to the specified parameters.
+ *
+ * <p>The server will be returned asynchronously through the provided callbacks.
+ */
+ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+ final IDhcpServerCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
private class NetworkStackConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/core/java/android/net/dhcp/DhcpServerCallbacks.java
new file mode 100644
index 0000000..bb56876
--- /dev/null
+++ b/core/java/android/net/dhcp/DhcpServerCallbacks.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.net.dhcp;
+
+/**
+ * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
+ * @hide
+ */
+public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
+ // TODO: add @Override here once the API is versioned
+
+ /**
+ * Get the version of the aidl interface implemented by the callbacks.
+ */
+ public int getInterfaceVersion() {
+ // TODO: return IDhcpServerCallbacks.VERSION;
+ return 0;
+ }
+}
diff --git a/core/java/android/net/dhcp/IDhcpServer.aidl b/core/java/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..559433b
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.INetworkStackStatusCallback;
+import android.net.dhcp.DhcpServingParamsParcel;
+
+/** @hide */
+oneway interface IDhcpServer {
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_INVALID_ARGUMENT = 2;
+ const int STATUS_UNKNOWN_ERROR = 3;
+
+ void start(in INetworkStackStatusCallback cb);
+ void updateParams(in DhcpServingParamsParcel params, in INetworkStackStatusCallback cb);
+ void stop(in INetworkStackStatusCallback cb);
+}
diff --git a/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..7ab4dcd
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.dhcp.IDhcpServer;
+
+/** @hide */
+oneway interface IDhcpServerCallbacks {
+ void onDhcpServerCreated(int statusCode, in IDhcpServer server);
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 9939a3c..1ebb551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -273,6 +273,30 @@
public static final native int getCallingUid();
/**
+ * Returns {@code true} if the current thread is currently executing an
+ * incoming transaction.
+ *
+ * @hide
+ */
+ @CriticalNative
+ public static final native boolean isHandlingTransaction();
+
+ /**
+ * Return the Linux uid assigned to the process that sent the transaction
+ * currently being processed.
+ *
+ * @throws IllegalStateException if the current thread is not currently
+ * executing an incoming transaction.
+ */
+ public static final int getCallingUidOrThrow() {
+ if (!isHandlingTransaction()) {
+ throw new IllegalStateException(
+ "Thread is not in a binder transcation");
+ }
+ return getCallingUid();
+ }
+
+ /**
* Return the UserHandle assigned to the process that sent you the
* current transaction that is being processed. This is the user
* of the caller. It is distinct from {@link #getCallingUid()} in that a
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 984846e..0d7223d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -970,7 +970,7 @@
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets,
+ mFinalStableInsets,
getResources().getConfiguration().isScreenRound(), false,
mDispatchedDisplayCutout);
if (DEBUG) {
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index 7eef63e..d051ed8 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -16,7 +16,9 @@
package android.util;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.time.Duration;
import java.time.format.DateTimeParseException;
@@ -212,4 +214,163 @@
}
return def;
}
+
+ /** Represents an integer config value. */
+ public static class IntValue {
+ private final String mKey;
+ private final int mDefaultValue;
+ private int mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public IntValue(String key, int defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getInt(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public int getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public int getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(int value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
+
+ /** Represents an long config value. */
+ public static class LongValue {
+ private final String mKey;
+ private final long mDefaultValue;
+ private long mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public LongValue(String key, long defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getLong(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public long getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public long getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(long value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
+
+ /** Represents an string config value. */
+ public static class StringValue {
+ private final String mKey;
+ private final String mDefaultValue;
+ private String mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public StringValue(String key, String defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getString(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public String getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public String getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(String value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 885b3e9..0931914 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.WindowInsets.Type.indexOf;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Insets;
@@ -114,8 +116,8 @@
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout,
@Nullable @InsetSide SparseIntArray typeSideMap) {
- Insets systemInsets = Insets.NONE;
- Insets maxInsets = Insets.NONE;
+ Insets[] typeInsetsMap = new Insets[Type.SIZE];
+ Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -123,32 +125,38 @@
if (source == null) {
continue;
}
- systemInsets = processSource(source, systemInsets, relativeFrame,
- false /* ignoreVisibility */, typeSideMap);
+ processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
+ typeSideMap);
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
if (source.getType() != TYPE_IME) {
- maxInsets = processSource(source, maxInsets, relativeFrameMax,
- true /* ignoreVisibility */, null /* typeSideMap */);
+ processSource(source, relativeFrameMax, true /* ignoreVisibility */,
+ typeMaxInsetsMap, null /* typeSideMap */);
}
}
- return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+ return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, isScreenRound,
alwaysConsumeNavBar, cutout);
}
- private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
- boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
- Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
- insets = Insets.add(currentInsets, insets);
- relativeFrame.inset(insets);
- if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
- @InsetSide int insetSide = getInsetSide(currentInsets);
+ private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
+ Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap) {
+ Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
+
+ int index = indexOf(toPublicType(source.getType()));
+ Insets existing = typeInsetsMap[index];
+ if (existing == null) {
+ typeInsetsMap[index] = insets;
+ } else {
+ typeInsetsMap[index] = Insets.max(existing, insets);
+ }
+
+ if (typeSideMap != null && !Insets.NONE.equals(insets)) {
+ @InsetSide int insetSide = getInsetSide(insets);
if (insetSide != INSET_SIDE_UNKNWON) {
- typeSideMap.put(source.getType(), getInsetSide(currentInsets));
+ typeSideMap.put(source.getType(), getInsetSide(insets));
}
}
- return insets;
}
/**
@@ -229,6 +237,21 @@
return result;
}
+ static @InsetType int toPublicType(@InternalInsetType int type) {
+ switch (type) {
+ case TYPE_TOP_BAR:
+ return Type.TOP_BAR;
+ case TYPE_SIDE_BAR_1:
+ case TYPE_SIDE_BAR_2:
+ case TYPE_SIDE_BAR_3:
+ return Type.SIDE_BARS;
+ case TYPE_IME:
+ return Type.IME;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
public static boolean getDefaultVisibly(@InsetType int type) {
switch (type) {
case TYPE_TOP_BAR:
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9dfbf28..cb2c40e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -939,6 +939,26 @@
*/
private static boolean sAcceptZeroSizeDragShadow;
+ /**
+ * Prior to Q, {@link #dispatchApplyWindowInsets} had some issues:
+ * <ul>
+ * <li>The modified insets changed by {@link #onApplyWindowInsets} were passed to the
+ * entire view hierarchy in prefix order, including siblings as well as siblings of parents
+ * further down the hierarchy. This violates the basic concepts of the view hierarchy, and
+ * thus, the hierarchical dispatching mechanism was hard to use for apps.</li>
+ *
+ * <li>Dispatch was stopped after the insets were fully consumed. This is somewhat confusing
+ * for developers, but more importantly, by adding more granular information to
+ * {@link WindowInsets} it becomes really cumbersome to define what consumed actually means
+ * </li>
+ * </ul>
+ *
+ * In order to make window inset dispatching work properly, we dispatch window insets
+ * in the view hierarchy in a proper hierarchical manner and don't stop dispatching if the
+ * insets are consumed if this flag is set to {@code false}.
+ */
+ static boolean sBrokenInsetsDispatch;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -5108,6 +5128,9 @@
sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+ sBrokenInsetsDispatch = !ViewRootImpl.USE_NEW_INSETS
+ || targetSdkVersion < Build.VERSION_CODES.Q;
+
sCompatibilityDone = true;
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8372032..9d11397 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7111,6 +7111,14 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
insets = super.dispatchApplyWindowInsets(insets);
+ if (View.sBrokenInsetsDispatch) {
+ return brokenDispatchApplyWindowInsets(insets);
+ } else {
+ return newDispatchApplyWindowInsets(insets);
+ }
+ }
+
+ private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) {
if (!insets.isConsumed()) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
@@ -7123,6 +7131,14 @@
return insets;
}
+ private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).dispatchApplyWindowInsets(insets);
+ }
+ return insets;
+ }
+
/**
* Returns the animation listener to which layout animation events are
* sent.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c0b4283..8e4dc67 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1859,8 +1859,7 @@
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
} else {
- mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets,
+ mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 572d331..b3da727 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,18 +17,33 @@
package android.view;
-import android.annotation.NonNull;
+import static android.view.WindowInsets.Type.FIRST;
+import static android.view.WindowInsets.Type.IME;
+import static android.view.WindowInsets.Type.LAST;
+import static android.view.WindowInsets.Type.SIDE_BARS;
+import static android.view.WindowInsets.Type.SIZE;
+import static android.view.WindowInsets.Type.TOP_BAR;
+import static android.view.WindowInsets.Type.all;
+import static android.view.WindowInsets.Type.compatSystemInsets;
+import static android.view.WindowInsets.Type.indexOf;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsState.InternalInsetType;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -49,9 +64,9 @@
*/
public final class WindowInsets {
- @NonNull private final Insets mSystemWindowInsets;
- @NonNull private final Insets mWindowDecorInsets;
- @NonNull private final Insets mStableInsets;
+ private final Insets[] mTypeInsetsMap;
+ private final Insets[] mTypeMaxInsetsMap;
+
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@Nullable private final DisplayCutout mDisplayCutout;
@@ -64,7 +79,6 @@
private final boolean mAlwaysConsumeNavBar;
private final boolean mSystemWindowInsetsConsumed;
- private final boolean mWindowDecorInsetsConsumed;
private final boolean mStableInsetsConsumed;
private final boolean mDisplayCutoutConsumed;
@@ -78,7 +92,7 @@
public static final WindowInsets CONSUMED;
static {
- CONSUMED = new WindowInsets((Insets) null, null, null, false, false, null);
+ CONSUMED = new WindowInsets((Rect) null, null, false, false, null);
}
/**
@@ -87,24 +101,38 @@
* A {@code null} inset indicates that the respective inset is consumed.
*
* @hide
+ * @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
*/
- public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+ public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
- this(insetsOrNull(systemWindowInsets), insetsOrNull(windowDecorInsets),
- insetsOrNull(stableInsets), isRound, alwaysConsumeNavBar, displayCutout);
+ this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
+ isRound, alwaysConsumeNavBar, displayCutout);
}
- private WindowInsets(Insets systemWindowInsets, Insets windowDecorInsets,
- Insets stableInsets, boolean isRound, boolean alwaysConsumeNavBar,
- DisplayCutout displayCutout) {
- mSystemWindowInsetsConsumed = systemWindowInsets == null;
- mSystemWindowInsets = mSystemWindowInsetsConsumed ? Insets.NONE : systemWindowInsets;
+ /**
+ * Construct a new WindowInsets from individual insets.
+ *
+ * {@code typeInsetsMap} and {@code typeMaxInsetsMap} are a map of indexOf(type) -> insets that
+ * contain the information what kind of system bars causes how much insets. The insets in this
+ * map are non-additive; i.e. they have the same origin. In other words: If two system bars
+ * overlap on one side, the insets of the larger bar will also include the insets of the smaller
+ * bar.
+ *
+ * {@code null} type inset map indicates that the respective inset is fully consumed.
+ * @hide
+ */
+ public WindowInsets(@Nullable Insets[] typeInsetsMap,
+ @Nullable Insets[] typeMaxInsetsMap, boolean isRound,
+ boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
+ mSystemWindowInsetsConsumed = typeInsetsMap == null;
+ mTypeInsetsMap = mSystemWindowInsetsConsumed
+ ? new Insets[SIZE]
+ : typeInsetsMap.clone();
- mWindowDecorInsetsConsumed = windowDecorInsets == null;
- mWindowDecorInsets = mWindowDecorInsetsConsumed ? Insets.NONE : windowDecorInsets;
-
- mStableInsetsConsumed = stableInsets == null;
- mStableInsets = mStableInsetsConsumed ? Insets.NONE : stableInsets;
+ mStableInsetsConsumed = typeMaxInsetsMap == null;
+ mTypeMaxInsetsMap = mStableInsetsConsumed
+ ? new Insets[SIZE]
+ : typeMaxInsetsMap.clone();
mIsRound = isRound;
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
@@ -120,10 +148,7 @@
* @param src Source to copy insets from
*/
public WindowInsets(WindowInsets src) {
- this(src.mSystemWindowInsetsConsumed ? null : src.mSystemWindowInsets,
- src.mWindowDecorInsetsConsumed ? null : src.mWindowDecorInsets,
- src.mStableInsetsConsumed ? null : src.mStableInsets,
- src.mIsRound, src.mAlwaysConsumeNavBar,
+ this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mIsRound, src.mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(src));
}
@@ -137,10 +162,64 @@
}
}
+ /**
+ * @return The insets that include system bars indicated by {@code typeMask}, taken from
+ * {@code typeInsetMap}.
+ */
+ private static Insets getInsets(Insets[] typeInsetsMap, @InsetType int typeMask) {
+ Insets result = null;
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ Insets insets = typeInsetsMap[indexOf(i)];
+ if (insets == null) {
+ continue;
+ }
+ if (result == null) {
+ result = insets;
+ } else {
+ result = Insets.max(result, insets);
+ }
+ }
+ return result == null ? Insets.NONE : result;
+ }
+
+ /**
+ * Sets all entries in {@code typeInsetsMap} that belong to {@code typeMask} to {@code insets},
+ */
+ private static void setInsets(Insets[] typeInsetsMap, @InsetType int typeMask, Insets insets) {
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ typeInsetsMap[indexOf(i)] = insets;
+ }
+ }
+
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(systemWindowInsets, null, null, false, false, null);
+ this(createCompatTypeMap(systemWindowInsets), null, false, false, null);
+ }
+
+ /**
+ * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
+ * {@link InsetType#topBar()} and {@link InsetType#sideBars()}, depending on the location of the
+ * inset.
+ */
+ private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
+ if (insets == null) {
+ return null;
+ }
+ Insets[] typeInsetMap = new Insets[SIZE];
+ assignCompatInsets(typeInsetMap, insets);
+ return typeInsetMap;
+ }
+
+ private static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
+ typeInsetMap[indexOf(TOP_BAR)] = Insets.of(0, insets.top, 0, 0);
+ typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
}
/**
@@ -156,8 +235,8 @@
if (mTempRect == null) {
mTempRect = new Rect();
}
- mTempRect.set(mSystemWindowInsets.left, mSystemWindowInsets.top,
- mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+ Insets insets = getSystemWindowInsets();
+ mTempRect.set(insets.left, insets.top, insets.right, insets.bottom);
return mTempRect;
}
@@ -172,7 +251,46 @@
*/
@NonNull
public Insets getSystemWindowInsets() {
- return mSystemWindowInsets;
+ return getInsets(mTypeInsetsMap, compatSystemInsets());
+ }
+
+ /**
+ * Returns the insets of a specific set of windows causing insets, denoted by the
+ * {@code typeMask} bit mask of {@link InsetType}s.
+ *
+ * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+ * @return The insets.
+ *
+ * @hide pending unhide
+ */
+ public Insets getInsets(@InsetType int typeMask) {
+ return getInsets(mTypeInsetsMap, typeMask);
+ }
+
+ /**
+ * Returns the maximum amount of insets a specific set of windows can cause, denoted by the
+ * {@code typeMask} bit mask of {@link InsetType}s.
+ *
+ * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+ * or fully obscured by the system window identified by {@code type}. This value does not
+ * change based on the visibility state of those elements. for example, if the status bar is
+ * normally shown, but temporarily hidden, the maximum inset will still provide the inset
+ * associated with the status bar being shown.</p>
+ *
+ * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+ * @return The insets.
+ *
+ * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Maximum
+ * insets are not available for this type as the height of the
+ * IME is dynamic depending on the {@link EditorInfo} of the
+ * currently focused view, as well as the UI state of the IME.
+ * @hide pending unhide
+ */
+ public Insets getMaxInsets(@InsetType int typeMask) throws IllegalArgumentException {
+ if ((typeMask & IME) != 0) {
+ throw new IllegalArgumentException("Unable to query the maximum insets for IME");
+ }
+ return getInsets(mTypeMaxInsetsMap, typeMask);
}
/**
@@ -185,7 +303,7 @@
* @return The left system window inset
*/
public int getSystemWindowInsetLeft() {
- return mSystemWindowInsets.left;
+ return getSystemWindowInsets().left;
}
/**
@@ -198,7 +316,7 @@
* @return The top system window inset
*/
public int getSystemWindowInsetTop() {
- return mSystemWindowInsets.top;
+ return getSystemWindowInsets().top;
}
/**
@@ -211,7 +329,7 @@
* @return The right system window inset
*/
public int getSystemWindowInsetRight() {
- return mSystemWindowInsets.right;
+ return getSystemWindowInsets().right;
}
/**
@@ -224,63 +342,7 @@
* @return The bottom system window inset
*/
public int getSystemWindowInsetBottom() {
- return mSystemWindowInsets.bottom;
- }
-
- /**
- * Returns the left window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The left window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetLeft() {
- return mWindowDecorInsets.left;
- }
-
- /**
- * Returns the top window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The top window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetTop() {
- return mWindowDecorInsets.top;
- }
-
- /**
- * Returns the right window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The right window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetRight() {
- return mWindowDecorInsets.right;
- }
-
- /**
- * Returns the bottom window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The bottom window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetBottom() {
- return mWindowDecorInsets.bottom;
+ return getSystemWindowInsets().bottom;
}
/**
@@ -293,23 +355,7 @@
* @return true if any of the system window inset values are nonzero
*/
public boolean hasSystemWindowInsets() {
- return mSystemWindowInsets.left != 0 || mSystemWindowInsets.top != 0 ||
- mSystemWindowInsets.right != 0 || mSystemWindowInsets.bottom != 0;
- }
-
- /**
- * Returns true if this WindowInsets has nonzero window decor insets.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return true if any of the window decor inset values are nonzero
- * @hide pending API
- */
- public boolean hasWindowDecorInsets() {
- return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
- mWindowDecorInsets.right != 0 || mWindowDecorInsets.bottom != 0;
+ return !getSystemWindowInsets().equals(Insets.NONE);
}
/**
@@ -318,7 +364,8 @@
* @return true if any inset values are nonzero
*/
public boolean hasInsets() {
- return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets()
+ return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
+ || !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
|| mDisplayCutout != null;
}
@@ -340,9 +387,7 @@
*/
@NonNull
public WindowInsets consumeDisplayCutout() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- mStableInsetsConsumed ? null : mStableInsets,
+ return new WindowInsets(mTypeInsetsMap, mTypeMaxInsetsMap,
mIsRound, mAlwaysConsumeNavBar,
null /* displayCutout */);
}
@@ -362,7 +407,7 @@
* @return true if the insets have been fully consumed.
*/
public boolean isConsumed() {
- return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed
+ return mSystemWindowInsetsConsumed && mStableInsetsConsumed
&& mDisplayCutoutConsumed;
}
@@ -387,9 +432,7 @@
*/
@NonNull
public WindowInsets consumeSystemWindowInsets() {
- return new WindowInsets(null /* systemWindowInsets */,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- mStableInsetsConsumed ? null : mStableInsets,
+ return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -449,18 +492,6 @@
}
/**
- * @hide
- */
- @NonNull
- public WindowInsets consumeWindowDecorInsets() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- null /* windowDecorInsets */,
- mStableInsetsConsumed ? null : mStableInsets,
- mIsRound, mAlwaysConsumeNavBar,
- displayCutoutCopyConstructorArgument(this));
- }
-
- /**
* Returns the stable insets in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
@@ -473,7 +504,7 @@
*/
@NonNull
public Insets getStableInsets() {
- return mStableInsets;
+ return getInsets(mTypeMaxInsetsMap, compatSystemInsets());
}
/**
@@ -488,7 +519,7 @@
* @return The top stable inset
*/
public int getStableInsetTop() {
- return mStableInsets.top;
+ return getStableInsets().top;
}
/**
@@ -503,7 +534,7 @@
* @return The left stable inset
*/
public int getStableInsetLeft() {
- return mStableInsets.left;
+ return getStableInsets().left;
}
/**
@@ -518,7 +549,7 @@
* @return The right stable inset
*/
public int getStableInsetRight() {
- return mStableInsets.right;
+ return getStableInsets().right;
}
/**
@@ -533,7 +564,7 @@
* @return The bottom stable inset
*/
public int getStableInsetBottom() {
- return mStableInsets.bottom;
+ return getStableInsets().bottom;
}
/**
@@ -548,8 +579,7 @@
* @return true if any of the stable inset values are nonzero
*/
public boolean hasStableInsets() {
- return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
- || mStableInsets.bottom != 0;
+ return !getStableInsets().equals(Insets.NONE);
}
/**
@@ -559,9 +589,7 @@
*/
@NonNull
public WindowInsets consumeStableInsets() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- null /* stableInsets */,
+ return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -575,9 +603,8 @@
@Override
public String toString() {
- return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
- + " windowDecorInsets=" + mWindowDecorInsets
- + " stableInsets=" + mStableInsets
+ return "WindowInsets{systemWindowInsets=" + getSystemWindowInsets()
+ + " stableInsets=" + getStableInsets()
+ (mDisplayCutout != null ? " cutout=" + mDisplayCutout : "")
+ (isRound() ? " round" : "")
+ "}";
@@ -634,16 +661,16 @@
Preconditions.checkArgumentNonnegative(bottom);
return new WindowInsets(
- mSystemWindowInsetsConsumed ? null :
- insetInsets(mSystemWindowInsets, left, top, right, bottom),
- mWindowDecorInsetsConsumed ? null :
- insetInsets(mWindowDecorInsets, left, top, right, bottom),
- mStableInsetsConsumed ? null :
- insetInsets(mStableInsets, left, top, right, bottom),
+ mSystemWindowInsetsConsumed
+ ? null
+ : insetInsets(mTypeInsetsMap, left, top, right, bottom),
+ mStableInsetsConsumed
+ ? null
+ : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
mIsRound, mAlwaysConsumeNavBar,
mDisplayCutoutConsumed
- ? null :
- mDisplayCutout == null
+ ? null
+ : mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom));
}
@@ -653,23 +680,49 @@
if (this == o) return true;
if (o == null || !(o instanceof WindowInsets)) return false;
WindowInsets that = (WindowInsets) o;
+
return mIsRound == that.mIsRound
&& mAlwaysConsumeNavBar == that.mAlwaysConsumeNavBar
&& mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
- && mWindowDecorInsetsConsumed == that.mWindowDecorInsetsConsumed
&& mStableInsetsConsumed == that.mStableInsetsConsumed
&& mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
- && Objects.equals(mSystemWindowInsets, that.mSystemWindowInsets)
- && Objects.equals(mWindowDecorInsets, that.mWindowDecorInsets)
- && Objects.equals(mStableInsets, that.mStableInsets)
+ && Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
+ && Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
}
@Override
public int hashCode() {
- return Objects.hash(mSystemWindowInsets, mWindowDecorInsets, mStableInsets, mIsRound,
- mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
- mWindowDecorInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
+ return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
+ mIsRound, mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
+ mStableInsetsConsumed, mDisplayCutoutConsumed);
+ }
+
+
+ /**
+ * Insets every inset in {@code typeInsetsMap} by the specified left, top, right, bottom.
+ *
+ * @return {@code typeInsetsMap} if no inset was modified; a copy of the map with the modified
+ * insets otherwise.
+ */
+ private static Insets[] insetInsets(
+ Insets[] typeInsetsMap, int left, int top, int right, int bottom) {
+ boolean cloned = false;
+ for (int i = 0; i < SIZE; i++) {
+ Insets insets = typeInsetsMap[i];
+ if (insets == null) {
+ continue;
+ }
+ Insets insetInsets = insetInsets(insets, left, top, right, bottom);
+ if (insetInsets != insets) {
+ if (!cloned) {
+ typeInsetsMap = typeInsetsMap.clone();
+ cloned = true;
+ }
+ typeInsetsMap[i] = insetInsets;
+ }
+ }
+ return typeInsetsMap;
}
private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
@@ -683,10 +736,6 @@
return Insets.of(newLeft, newTop, newRight, newBottom);
}
- private static Insets insetsOrNull(Rect insets) {
- return insets != null ? Insets.of(insets) : null;
- }
-
/**
* @return whether system window insets have been consumed.
*/
@@ -699,11 +748,13 @@
*/
public static class Builder {
- private Insets mSystemWindowInsets;
- private Insets mStableInsets;
+ private final Insets[] mTypeInsetsMap;
+ private final Insets[] mTypeMaxInsetsMap;
+ private boolean mSystemInsetsConsumed = true;
+ private boolean mStableInsetsConsumed = true;
+
private DisplayCutout mDisplayCutout;
- private Insets mWindowDecorInsets;
private boolean mIsRound;
private boolean mAlwaysConsumeNavBar;
@@ -711,6 +762,8 @@
* Creates a builder where all insets are initially consumed.
*/
public Builder() {
+ mTypeInsetsMap = new Insets[SIZE];
+ mTypeMaxInsetsMap = new Insets[SIZE];
}
/**
@@ -719,12 +772,11 @@
* @param insets the instance to initialize from.
*/
public Builder(WindowInsets insets) {
- mSystemWindowInsets = insets.mSystemWindowInsetsConsumed ? null
- : insets.mSystemWindowInsets;
- mStableInsets = insets.mStableInsetsConsumed ? null : insets.mStableInsets;
+ mTypeInsetsMap = insets.mTypeInsetsMap.clone();
+ mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
+ mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
+ mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
- mWindowDecorInsets = insets.mWindowDecorInsetsConsumed ? null
- : insets.mWindowDecorInsets;
mIsRound = insets.mIsRound;
mAlwaysConsumeNavBar = insets.mAlwaysConsumeNavBar;
}
@@ -742,7 +794,66 @@
@NonNull
public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
Preconditions.checkNotNull(systemWindowInsets);
- mSystemWindowInsets = systemWindowInsets;
+ assignCompatInsets(mTypeInsetsMap, systemWindowInsets.toRect());
+ mSystemInsetsConsumed = false;
+ return this;
+ }
+
+ /**
+ * Sets the insets of a specific window type in pixels.
+ *
+ * <p>The insets represents the area of a a window that is partially or fully obscured by
+ * the system windows identified by {@code typeMask}.
+ * </p>
+ *
+ * @see #getInsets(int)
+ *
+ * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+ * @param insets The insets to set.
+ *
+ * @return itself
+ * @hide pending unhide
+ */
+ @NonNull
+ public Builder setInsets(@InsetType int typeMask, @NonNull Insets insets) {
+ Preconditions.checkNotNull(insets);
+ WindowInsets.setInsets(mTypeInsetsMap, typeMask, insets);
+ mSystemInsetsConsumed = false;
+ return this;
+ }
+
+ /**
+ * Sets the maximum amount of insets a specific window type in pixels.
+ *
+ * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+ * or fully obscured by the system windows identified by {@code typeMask}. This value does
+ * not change based on the visibility state of those elements. for example, if the status
+ * bar is normally shown, but temporarily hidden, the maximum inset will still provide the
+ * inset associated with the status bar being shown.</p>
+ *
+ * @see #getMaxInsets(int)
+ *
+ * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+ * @param insets The insets to set.
+ *
+ * @return itself
+ *
+ * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
+ * insets are not available for this type as the height of
+ * the IME is dynamic depending on the {@link EditorInfo}
+ * of the currently focused view, as well as the UI
+ * state of the IME.
+ * @hide pending unhide
+ */
+ @NonNull
+ public Builder setMaxInsets(@InsetType int typeMask, @NonNull Insets insets)
+ throws IllegalArgumentException{
+ if (typeMask == IME) {
+ throw new IllegalArgumentException("Maximum inset not available for IME");
+ }
+ Preconditions.checkNotNull(insets);
+ WindowInsets.setInsets(mTypeMaxInsetsMap, typeMask, insets);
+ mStableInsetsConsumed = false;
return this;
}
@@ -761,7 +872,8 @@
@NonNull
public Builder setStableInsets(@NonNull Insets stableInsets) {
Preconditions.checkNotNull(stableInsets);
- mStableInsets = stableInsets;
+ assignCompatInsets(mTypeInsetsMap, stableInsets.toRect());
+ mStableInsetsConsumed = false;
return this;
}
@@ -780,14 +892,6 @@
/** @hide */
@NonNull
- public Builder setWindowDecorInsets(@NonNull Insets windowDecorInsets) {
- Preconditions.checkNotNull(windowDecorInsets);
- mWindowDecorInsets = windowDecorInsets;
- return this;
- }
-
- /** @hide */
- @NonNull
public Builder setRound(boolean round) {
mIsRound = round;
return this;
@@ -807,8 +911,9 @@
*/
@NonNull
public WindowInsets build() {
- return new WindowInsets(mSystemWindowInsets, mWindowDecorInsets, mStableInsets,
- mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
+ return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
+ mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mIsRound,
+ mAlwaysConsumeNavBar, mDisplayCutout);
}
}
@@ -818,10 +923,31 @@
*/
public static final class Type {
- static final int TOP_BAR = 0x1;
+ static final int FIRST = 0x1;
+ static final int TOP_BAR = FIRST;
+
static final int IME = 0x2;
static final int SIDE_BARS = 0x4;
- static final int WINDOW_DECOR = 0x8;
+
+ static final int LAST = 0x8;
+ static final int SIZE = 4;
+ static final int WINDOW_DECOR = LAST;
+
+ static int indexOf(@InsetType int type) {
+ switch (type) {
+ case TOP_BAR:
+ return 0;
+ case IME:
+ return 1;
+ case SIDE_BARS:
+ return 2;
+ case WINDOW_DECOR:
+ return 3;
+ default:
+ throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
+ + " type=" + type);
+ }
+ }
private Type() {
}
@@ -870,6 +996,15 @@
}
/**
+ * @return Inset types representing the list of bars that traditionally were denoted as
+ * system insets.
+ * @hide
+ */
+ static @InsetType int compatSystemInsets() {
+ return TOP_BAR | SIDE_BARS | IME;
+ }
+
+ /**
* @return All inset types combined.
*/
public static @InsetType int all() {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 44a381e..8be887b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -199,6 +199,8 @@
final int flags = 0; // TODO(b/111276913): get proper flags
try {
+ if (mSystemServerInterface == null) return;
+
mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
componentName, mId, flags, new IResultReceiver.Stub() {
@Override
@@ -383,6 +385,8 @@
}
try {
+ if (mSystemServerInterface == null) return;
+
mSystemServerInterface.finishSession(mContext.getUserId(), mId);
} catch (RemoteException e) {
Log.e(TAG, "Error destroying system-service session " + mId + " for "
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
index 067513f..3996b27 100644
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ b/core/java/android/view/textclassifier/GenerateLinksLogger.java
@@ -39,7 +39,6 @@
public final class GenerateLinksLogger {
private static final String LOG_TAG = "GenerateLinksLogger";
- private static final boolean DEBUG_LOG_ENABLED = false;
private static final String ZERO = "0";
private final MetricsLogger mMetricsLogger;
@@ -128,8 +127,9 @@
}
private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
+ if (!Log.ENABLE_FULL_LOGGING) {
+ return;
+ }
final String callId = Objects.toString(
log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
final String entityType = Objects.toString(
@@ -143,7 +143,7 @@
final int latencyMs = Integer.parseInt(
Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
- Log.d(LOG_TAG,
+ Log.v(LOG_TAG,
String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
}
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index ef19ee5..5c60c09 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -16,10 +16,12 @@
package android.view.textclassifier;
-import android.util.Slog;
-
/**
* Logging for android.view.textclassifier package.
+ * <p>
+ * To enable full log:
+ * 1. adb shell setprop log.tag.androidtc VERBOSE
+ * 2. adb shell stop && adb shell start
*/
final class Log {
@@ -27,24 +29,32 @@
* true: Enables full logging.
* false: Limits logging to debug level.
*/
- private static final boolean ENABLE_FULL_LOGGING = false;
+ static final boolean ENABLE_FULL_LOGGING =
+ android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
- private Log() {}
+ private Log() {
+ }
+
+ public static void v(String tag, String msg) {
+ if (ENABLE_FULL_LOGGING) {
+ android.util.Log.v(tag, msg);
+ }
+ }
public static void d(String tag, String msg) {
- Slog.d(tag, msg);
+ android.util.Log.d(tag, msg);
}
public static void w(String tag, String msg) {
- Slog.w(tag, msg);
+ android.util.Log.w(tag, msg);
}
public static void e(String tag, String msg, Throwable tr) {
if (ENABLE_FULL_LOGGING) {
- Slog.e(tag, msg, tr);
+ android.util.Log.e(tag, msg, tr);
} else {
final String trString = (tr != null) ? tr.getClass().getSimpleName() : "??";
- Slog.d(tag, String.format("%s (%s)", msg, trString));
+ android.util.Log.d(tag, String.format("%s (%s)", msg, trString));
}
}
}
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index cdacdd5..48a568a 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -39,7 +39,6 @@
public final class SelectionSessionLogger {
private static final String LOG_TAG = "SelectionSessionLogger";
- private static final boolean DEBUG_LOG_ENABLED = false;
static final String CLASSIFIER_ID = "androidtc";
private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
@@ -195,8 +194,9 @@
}
private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
+ if (!Log.ENABLE_FULL_LOGGING) {
+ return;
+ }
final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
final String widget = widgetVersion.isEmpty()
@@ -221,7 +221,7 @@
final int eventEnd = Integer.parseInt(
Objects.toString(log.getTaggedData(EVENT_END), ZERO));
- Log.d(LOG_TAG,
+ Log.v(LOG_TAG,
String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
widget, model));
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 4c64198..45668c0 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -27,7 +27,6 @@
@WorkerThread
final class TextClassificationSession implements TextClassifier {
- /* package */ static final boolean DEBUG_LOG_ENABLED = true;
private static final String LOG_TAG = "TextClassificationSession";
private final TextClassifier mDelegate;
@@ -133,9 +132,7 @@
if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
&& mStartEvent == null) {
- if (DEBUG_LOG_ENABLED) {
- Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
- }
+ Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
return false;
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
index 1558446..439e594 100644
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -39,7 +39,6 @@
public final class TextClassifierEventTronLogger {
private static final String TAG = "TCEventTronLogger";
- private static final boolean DEBUG_LOG_ENABLED = false;
private final MetricsLogger mMetricsLogger;
@@ -125,7 +124,7 @@
}
private void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) {
+ if (!Log.ENABLE_FULL_LOGGING) {
return;
}
final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
@@ -147,6 +146,6 @@
builder.append(", model=").append(model);
builder.append(", entityType=").append(entityType);
- Log.d(TAG, builder.toString());
+ Log.v(TAG, builder.toString());
}
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 6a3a8f0..dc9a585 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -454,6 +454,7 @@
(RelativeLayout.LayoutParams) mAmPmLayout.getLayoutParams();
if (params.getRule(RelativeLayout.RIGHT_OF) != 0
|| params.getRule(RelativeLayout.LEFT_OF) != 0) {
+ final int margin = (int) (mContext.getResources().getDisplayMetrics().density * 8);
// Horizontal mode, with AM/PM appearing to left/right of hours and minutes.
final boolean isAmPmAtLeft;
if (TextUtils.getLayoutDirectionFromLocale(mLocale) == View.LAYOUT_DIRECTION_LTR) {
@@ -461,10 +462,6 @@
} else {
isAmPmAtLeft = !isAmPmAtStart;
}
- if (mIsAmPmAtLeft == isAmPmAtLeft) {
- // AM/PM is already at the correct location. No change needed.
- return;
- }
if (isAmPmAtLeft) {
params.removeRule(RelativeLayout.RIGHT_OF);
@@ -473,6 +470,14 @@
params.removeRule(RelativeLayout.LEFT_OF);
params.addRule(RelativeLayout.RIGHT_OF, mMinuteView.getId());
}
+
+ if (isAmPmAtStart) {
+ params.setMarginStart(0);
+ params.setMarginEnd(margin);
+ } else {
+ params.setMarginStart(margin);
+ params.setMarginEnd(0);
+ }
mIsAmPmAtLeft = isAmPmAtLeft;
} else if (params.getRule(RelativeLayout.BELOW) != 0
|| params.getRule(RelativeLayout.ABOVE) != 0) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 3329e20..f201ceb 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -875,6 +875,11 @@
return IPCThreadState::self()->getCallingUid();
}
+static jboolean android_os_Binder_isHandlingTransaction()
+{
+ return IPCThreadState::self()->isServingCall();
+}
+
static jlong android_os_Binder_clearCallingIdentity()
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -960,6 +965,8 @@
// @CriticalNative
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
// @CriticalNative
+ { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
+ // @CriticalNative
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
// @CriticalNative
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index adb2b62..30c10b1 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -76,16 +76,14 @@
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/minutes"
android:layout_alignBaseline="@+id/minutes"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="0dp"
android:orientation="vertical"
android:baselineAlignedChildIndex="1">
<RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:layout_marginBottom="-8dp"
@@ -101,8 +99,6 @@
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 534c5cd..6c9c3c1 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -43,4 +43,13 @@
Binder.restoreCallingWorkSource(token);
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+
+ @SmallTest
+ public void testGetCallingUidOrThrow() throws Exception {
+ try {
+ Binder.getCallingUidOrThrow();
+ throw new AssertionError("IllegalStateException expected");
+ } catch (IllegalStateException expected) {
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9807f26..95af525 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -29,12 +29,14 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseIntArray;
+import android.view.WindowInsets.Type;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,9 +58,12 @@
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, typeSideMap);
- assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
}
@Test
@@ -70,7 +75,11 @@
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, null);
assertEquals(100, insets.getStableInsetBottom());
- assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
}
@Test
@@ -81,7 +90,9 @@
mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, null);
- assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
}
@Test
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 1c2df2c..1513a1a 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -16,12 +16,19 @@
package android.view;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.sideBars;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowInsets.Builder;
+import android.view.WindowInsets.Type;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,13 +40,13 @@
@Test
public void systemWindowInsets_afterConsuming_isConsumed() {
- assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, null, false, false, null)
+ assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, false, false, null)
.consumeSystemWindowInsets().isConsumed());
}
@Test
public void multiNullConstructor_isConsumed() {
- assertTrue(new WindowInsets(null, null, null, false, false, null).isConsumed());
+ assertTrue(new WindowInsets((Rect) null, null, false, false, null).isConsumed());
}
@Test
@@ -47,4 +54,12 @@
assertTrue(new WindowInsets((Rect) null).isConsumed());
}
+ @Test
+ public void typeMap() {
+ Builder b = new WindowInsets.Builder();
+ b.setInsets(sideBars(), Insets.of(0, 0, 0, 100));
+ b.setInsets(ime(), Insets.of(0, 0, 0, 300));
+ WindowInsets insets = b.build();
+ assertEquals(300, insets.getSystemWindowInsets().bottom);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index 218566e..d782c0c 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -167,7 +167,7 @@
}
private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
- return new WindowInsets(content.toRect(), null, null, false, false, cutout);
+ return new WindowInsets(content.toRect(), null, false, false, cutout);
}
private ViewGroup createViewGroupWithId(int id) {
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c5afc95..fbe613d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -242,6 +242,32 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.mainline.networkstack">
+ <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
+ <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
+ <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.CONTROL_VPN"/>
+ <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/>
+ <permission name="android.permission.MANAGE_NETWORK_POLICY"/>
+ <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
+ <permission name="android.permission.NETWORK_SETTINGS"/>
+ <permission name="android.permission.NETWORK_STACK" />
+ <permission name="android.permission.NET_TUNNELING"/>
+ <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+ <permission name="android.permission.PEERS_MAC_ADDRESS"/>
+ <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
+ <permission name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.server.telecom">
<permission name="android.permission.BIND_CONNECTION_SERVICE"/>
<permission name="android.permission.BIND_INCALL_SERVICE"/>
diff --git a/native/android/net.c b/native/android/net.c
index 4cac371..a8104fc 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,26 +84,28 @@
return android_getaddrinfofornet(node, service, hints, netid, 0, res);
}
-int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname,
+ int ns_class, int ns_type, enum ResNsendFlags flags) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
}
- return resNetworkQuery(netid, dname, ns_class, ns_type);
+ return resNetworkQuery(netid, dname, ns_class, ns_type, flags);
}
int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
return resNetworkResult(fd, rcode, answer, anslen);
}
-int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen,
+ enum ResNsendFlags flags) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
}
- return resNetworkSend(netid, msg, msglen);
+ return resNetworkSend(netid, msg, msglen, flags);
}
void android_res_cancel(int nsend_fd) {
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 55bb517..4688848 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -22,6 +22,10 @@
srcs: [
"src/**/*.java",
],
+ static_libs: [
+ "dhcp-packet-lib",
+ "frameworks-net-shared-utils",
+ ]
}
// Updatable network stack packaged as an application
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpLease.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
similarity index 99%
rename from services/net/java/android/net/dhcp/DhcpLeaseRepository.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
index b3d0512..0d298de 100644
--- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -21,7 +21,8 @@
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
import static android.net.dhcp.DhcpLease.inet4AddrToString;
-import static android.net.util.NetworkConstants.IPV4_ADDR_BITS;
+
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
import static java.lang.Math.min;
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpPacketListener.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
similarity index 85%
rename from services/net/java/android/net/dhcp/DhcpServer.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 641bba2..14e2936 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -23,7 +23,8 @@
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
@@ -32,21 +33,28 @@
import static android.system.OsConstants.SO_BROADCAST;
import static android.system.OsConstants.SO_REUSEADDR;
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+
import static java.lang.Integer.toUnsignedLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
import android.net.MacAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.SharedLog;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -70,7 +78,7 @@
* on the looper asynchronously.
* @hide
*/
-public class DhcpServer {
+public class DhcpServer extends IDhcpServer.Stub {
private static final String REPO_TAG = "Repository";
// Lease time to transmit to client instead of a negative time in case a lease expired before
@@ -82,7 +90,7 @@
private static final int CMD_UPDATE_PARAMS = 3;
@NonNull
- private final ServerHandler mHandler;
+ private final HandlerThread mHandlerThread;
@NonNull
private final String mIfName;
@NonNull
@@ -93,10 +101,14 @@
private final Dependencies mDeps;
@NonNull
private final Clock mClock;
- @NonNull
- private final DhcpPacketListener mPacketListener;
@Nullable
+ private volatile ServerHandler mHandler;
+
+ // Accessed only on the handler thread
+ @Nullable
+ private DhcpPacketListener mPacketListener;
+ @Nullable
private FileDescriptor mSocket;
@NonNull
private DhcpServingParams mServingParams;
@@ -156,6 +168,12 @@
*/
void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
+
+ /**
+ * Verify that the caller is allowed to call public methods on DhcpServer.
+ * @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
+ */
+ void checkCaller() throws SecurityException;
}
private class DependenciesImpl implements Dependencies {
@@ -189,6 +207,11 @@
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
}
+
+ @Override
+ public void checkCaller() {
+ checkNetworkStackCallingPermission();
+ }
}
private static class MalformedPacketException extends Exception {
@@ -197,41 +220,62 @@
}
}
- public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+ public DhcpServer(@NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(looper, ifName, params, log, null);
+ this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
+ ifName, params, log, null);
}
@VisibleForTesting
- DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+ DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log,
@Nullable Dependencies deps) {
if (deps == null) {
deps = new DependenciesImpl();
}
- mHandler = new ServerHandler(looper);
+ mHandlerThread = handlerThread;
mIfName = ifName;
mServingParams = params;
mLog = log;
mDeps = deps;
mClock = deps.makeClock();
- mPacketListener = deps.makePacketListener();
mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
}
/**
* Start listening for and responding to packets.
+ *
+ * <p>It is not legal to call this method more than once; in particular the server cannot be
+ * restarted after being stopped.
*/
- public void start() {
- mHandler.sendEmptyMessage(CMD_START_DHCP_SERVER);
+ @Override
+ public void start(@Nullable INetworkStackStatusCallback cb) {
+ mDeps.checkCaller();
+ mHandlerThread.start();
+ mHandler = new ServerHandler(mHandlerThread.getLooper());
+ sendMessage(CMD_START_DHCP_SERVER, cb);
}
/**
* Update serving parameters. All subsequently received requests will be handled with the new
* parameters, and current leases that are incompatible with the new parameters are dropped.
*/
- public void updateParams(@NonNull DhcpServingParams params) {
- sendMessage(CMD_UPDATE_PARAMS, params);
+ @Override
+ public void updateParams(@Nullable DhcpServingParamsParcel params,
+ @Nullable INetworkStackStatusCallback cb) throws RemoteException {
+ mDeps.checkCaller();
+ final DhcpServingParams parsedParams;
+ try {
+ // throws InvalidParameterException with null params
+ parsedParams = DhcpServingParams.fromParcelableObject(params);
+ } catch (DhcpServingParams.InvalidParameterException e) {
+ mLog.e("Invalid parameters sent to DhcpServer", e);
+ if (cb != null) {
+ cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
+ }
+ return;
+ }
+ sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
}
/**
@@ -240,11 +284,17 @@
* <p>As the server is stopped asynchronously, some packets may still be processed shortly after
* calling this method.
*/
- public void stop() {
- mHandler.sendEmptyMessage(CMD_STOP_DHCP_SERVER);
+ @Override
+ public void stop(@Nullable INetworkStackStatusCallback cb) {
+ mDeps.checkCaller();
+ sendMessage(CMD_STOP_DHCP_SERVER, cb);
}
private void sendMessage(int what, @Nullable Object obj) {
+ if (mHandler == null) {
+ mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
+ return;
+ }
mHandler.sendMessage(mHandler.obtainMessage(what, obj));
}
@@ -255,23 +305,42 @@
@Override
public void handleMessage(@NonNull Message msg) {
+ final INetworkStackStatusCallback cb;
switch (msg.what) {
case CMD_UPDATE_PARAMS:
- final DhcpServingParams params = (DhcpServingParams) msg.obj;
+ final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
+ (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
+ final DhcpServingParams params = pair.first;
mServingParams = params;
mLeaseRepo.updateParams(
DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
params.excludedAddrs,
params.dhcpLeaseTimeSecs);
+
+ cb = pair.second;
break;
case CMD_START_DHCP_SERVER:
- // This is a no-op if the listener is already started
+ mPacketListener = mDeps.makePacketListener();
mPacketListener.start();
+ cb = (INetworkStackStatusCallback) msg.obj;
break;
case CMD_STOP_DHCP_SERVER:
- // This is a no-op if the listener was not started
- mPacketListener.stop();
+ if (mPacketListener != null) {
+ mPacketListener.stop();
+ mPacketListener = null;
+ }
+ mHandlerThread.quitSafely();
+ cb = (INetworkStackStatusCallback) msg.obj;
break;
+ default:
+ return;
+ }
+ if (cb != null) {
+ try {
+ cb.onStatusAvailable(STATUS_SUCCESS);
+ } catch (RemoteException e) {
+ mLog.e("Could not send status back to caller", e);
+ }
}
}
}
@@ -572,7 +641,7 @@
return mSocket;
} catch (IOException | ErrnoException e) {
mLog.e("Error creating UDP socket", e);
- DhcpServer.this.stop();
+ DhcpServer.this.stop(null);
return null;
} finally {
TrafficStats.setThreadStatsTag(oldTag);
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
similarity index 95%
rename from services/net/java/android/net/dhcp/DhcpServingParams.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index 2780814a..f38888a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -18,9 +18,10 @@
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.NetworkUtils.intToInet4AddressHTH;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
+import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
import static java.lang.Integer.toUnsignedLong;
@@ -107,9 +108,13 @@
/**
* Create parameters from a stable AIDL-compatible parcel.
+ * @throws InvalidParameterException The parameters parcelable is null or invalid.
*/
- public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel)
+ public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
throws InvalidParameterException {
+ if (parcel == null) {
+ throw new InvalidParameterException("Null serving parameters");
+ }
final LinkAddress serverAddr = new LinkAddress(
intToInet4AddressHTH(parcel.serverAddr),
parcel.serverAddrPrefixLength);
diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java
new file mode 100644
index 0000000..74bc147
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/util/SharedLog.java
@@ -0,0 +1,197 @@
+/*
+ * 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.net.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+
+/**
+ * Class to centralize logging functionality for tethering.
+ *
+ * All access to class methods other than dump() must be on the same thread.
+ *
+ * @hide
+ */
+public class SharedLog {
+ private static final int DEFAULT_MAX_RECORDS = 500;
+ private static final String COMPONENT_DELIMITER = ".";
+
+ private enum Category {
+ NONE,
+ ERROR,
+ MARK,
+ WARN,
+ };
+
+ private final LocalLog mLocalLog;
+ // The tag to use for output to the system log. This is not output to the
+ // LocalLog because that would be redundant.
+ private final String mTag;
+ // The component (or subcomponent) of a system that is sharing this log.
+ // This can grow in depth if components call forSubComponent() to obtain
+ // their SharedLog instance. The tag is not included in the component for
+ // brevity.
+ private final String mComponent;
+
+ public SharedLog(String tag) {
+ this(DEFAULT_MAX_RECORDS, tag);
+ }
+
+ public SharedLog(int maxRecords, String tag) {
+ this(new LocalLog(maxRecords), tag, tag);
+ }
+
+ private SharedLog(LocalLog localLog, String tag, String component) {
+ mLocalLog = localLog;
+ mTag = tag;
+ mComponent = component;
+ }
+
+ /**
+ * Create a SharedLog based on this log with an additional component prefix on each logged line.
+ */
+ public SharedLog forSubComponent(String component) {
+ if (!isRootLogInstance()) {
+ component = mComponent + COMPONENT_DELIMITER + component;
+ }
+ return new SharedLog(mLocalLog, mTag, component);
+ }
+
+ /**
+ * Dump the contents of this log.
+ *
+ * <p>This method may be called on any thread.
+ */
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
+ }
+
+ //////
+ // Methods that both log an entry and emit it to the system log.
+ //////
+
+ /**
+ * Log an error due to an exception. This does not include the exception stacktrace.
+ *
+ * <p>The log entry will be also added to the system log.
+ * @see #e(String, Throwable)
+ */
+ public void e(Exception e) {
+ Log.e(mTag, record(Category.ERROR, e.toString()));
+ }
+
+ /**
+ * Log an error message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void e(String msg) {
+ Log.e(mTag, record(Category.ERROR, msg));
+ }
+
+ /**
+ * Log an error due to an exception, with the exception stacktrace if provided.
+ *
+ * <p>The error and exception message appear in the shared log, but the stacktrace is only
+ * logged in general log output (logcat). The log entry will be also added to the system log.
+ */
+ public void e(@NonNull String msg, @Nullable Throwable exception) {
+ if (exception == null) {
+ e(msg);
+ return;
+ }
+ Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
+ }
+
+ /**
+ * Log an informational message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void i(String msg) {
+ Log.i(mTag, record(Category.NONE, msg));
+ }
+
+ /**
+ * Log a warning message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void w(String msg) {
+ Log.w(mTag, record(Category.WARN, msg));
+ }
+
+ //////
+ // Methods that only log an entry (and do NOT emit to the system log).
+ //////
+
+ /**
+ * Log a general message to be only included in the in-memory log.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ */
+ public void log(String msg) {
+ record(Category.NONE, msg);
+ }
+
+ /**
+ * Log a general, formatted message to be only included in the in-memory log.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ * @see String#format(String, Object...)
+ */
+ public void logf(String fmt, Object... args) {
+ log(String.format(fmt, args));
+ }
+
+ /**
+ * Log a message with MARK level.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ */
+ public void mark(String msg) {
+ record(Category.MARK, msg);
+ }
+
+ private String record(Category category, String msg) {
+ final String entry = logLine(category, msg);
+ mLocalLog.log(entry);
+ return entry;
+ }
+
+ private String logLine(Category category, String msg) {
+ final StringJoiner sj = new StringJoiner(" ");
+ if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
+ if (category != Category.NONE) sj.add(category.toString());
+ return sj.add(msg).toString();
+ }
+
+ // Check whether this SharedLog instance is nominally the top level in
+ // a potential hierarchy of shared logs (the root of a tree),
+ // or is a subcomponent within the hierarchy.
+ private boolean isRootLogInstance() {
+ return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
+ }
+}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 5afaf58..7fea1e0 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -16,15 +16,24 @@
package com.android.server;
-import static android.os.Binder.getCallingUid;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
import android.content.Intent;
import android.net.INetworkStackConnector;
+import android.net.dhcp.DhcpServer;
+import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.util.SharedLog;
import android.os.IBinder;
-import android.os.Process;
+import android.os.RemoteException;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -54,21 +63,37 @@
}
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
- // TODO: makeDhcpServer(), etc. will go here.
+ @NonNull
+ private final SharedLog mLog = new SharedLog(TAG);
+
+ @Override
+ public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
+ @NonNull IDhcpServerCallbacks cb) throws RemoteException {
+ checkNetworkStackCallingPermission();
+ final DhcpServer server;
+ try {
+ server = new DhcpServer(
+ ifName,
+ DhcpServingParams.fromParcelableObject(params),
+ mLog.forSubComponent(ifName + ".DHCP"));
+ } catch (DhcpServingParams.InvalidParameterException e) {
+ mLog.e("Invalid DhcpServingParams", e);
+ cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null);
+ return;
+ } catch (Exception e) {
+ mLog.e("Unknown error starting DhcpServer", e);
+ cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+ return;
+ }
+ cb.onDhcpServerCreated(STATUS_SUCCESS, server);
+ }
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
- checkCaller();
+ checkNetworkStackCallingPermission();
fout.println("NetworkStack logs:");
- // TODO: dump logs here
- }
- }
-
- private static void checkCaller() {
- // TODO: check that the calling PID is the system server.
- if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
- throw new SecurityException("Invalid caller: " + getCallingUid());
+ mLog.dump(fd, fout, args);
}
}
}
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
new file mode 100644
index 0000000..bb5900c
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
@@ -0,0 +1,45 @@
+/*
+ * 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.android.server.util;
+
+/**
+ * Network constants used by the network stack.
+ */
+public final class NetworkStackConstants {
+
+ /**
+ * IPv4 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc791
+ */
+ public static final int IPV4_ADDR_BITS = 32;
+ public static final int IPV4_MIN_MTU = 68;
+ public static final int IPV4_MAX_MTU = 65_535;
+
+ /**
+ * DHCP constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc2131
+ */
+ public static final int INFINITE_LEASE = 0xffffffff;
+
+ private NetworkStackConstants() {
+ throw new UnsupportedOperationException("This class is not to be instantiated");
+ }
+}
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
new file mode 100644
index 0000000..733f873
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -0,0 +1,42 @@
+/*
+ * 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.android.server.util;
+
+import static android.os.Binder.getCallingUid;
+
+import android.os.Process;
+
+/**
+ * Utility class to check calling permissions on the network stack.
+ */
+public final class PermissionUtil {
+
+ /**
+ * Check that the caller is allowed to communicate with the network stack.
+ * @throws SecurityException The caller is not allowed to communicate with the network stack.
+ */
+ public static void checkNetworkStackCallingPermission() {
+ // TODO: check that the calling PID is the system server.
+ if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
+ throw new SecurityException("Invalid caller: " + getCallingUid());
+ }
+ }
+
+ private PermissionUtil() {
+ throw new UnsupportedOperationException("This class is not to be instantiated");
+ }
+}
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
new file mode 100644
index 0000000..bd7ff2a
--- /dev/null
+++ b/packages/NetworkStack/tests/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+android_test {
+ name: "NetworkStackTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "android-support-test",
+ "mockito-target-extended-minus-junit4",
+ "NetworkStackLib",
+ "testables",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ]
+}
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidManifest.xml b/packages/NetworkStack/tests/AndroidManifest.xml
new file mode 100644
index 0000000..8b8474f
--- /dev/null
+++ b/packages/NetworkStack/tests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.networkstack.tests">
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.networkstack.tests"
+ android:label="Networking service tests">
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidTest.xml b/packages/NetworkStack/tests/AndroidTest.xml
new file mode 100644
index 0000000..6b08b57
--- /dev/null
+++ b/packages/NetworkStack/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for NetworkStack">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="NetworkStackTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="NetworkStackTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.networkstack.tests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
similarity index 99%
rename from tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
index ba0448c..51d50d9 100644
--- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -16,6 +16,7 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
@@ -29,7 +30,6 @@
import static org.mockito.Mockito.when;
import static java.lang.String.format;
-import static java.net.InetAddress.parseNumericAddress;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
similarity index 89%
rename from tests/net/java/android/net/dhcp/DhcpServerTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
index ab9bd84..d4c1e2e 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
@@ -16,11 +16,13 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -33,14 +35,14 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static java.net.InetAddress.parseNumericAddress;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
import android.net.LinkAddress;
import android.net.MacAddress;
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
@@ -48,9 +50,11 @@
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
import android.net.util.SharedLog;
-import android.os.test.TestLooper;
+import android.os.HandlerThread;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
import org.junit.Before;
@@ -67,10 +71,10 @@
import java.util.HashSet;
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@SmallTest
+@RunWithLooper
public class DhcpServerTest {
- private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
private static final String TEST_IFACE = "testiface";
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
@@ -113,18 +117,25 @@
private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
@NonNull
- private TestLooper mLooper;
+ private HandlerThread mHandlerThread;
+ @NonNull
+ private TestableLooper mLooper;
@NonNull
private DhcpServer mServer;
@Nullable
private String mPrevShareClassloaderProp;
+ private final INetworkStackStatusCallback mAssertSuccessCallback =
+ new INetworkStackStatusCallback.Stub() {
+ @Override
+ public void onStatusAvailable(int statusCode) {
+ assertEquals(STATUS_SUCCESS, statusCode);
+ }
+ };
+
@Before
public void setUp() throws Exception {
- // Allow mocking package-private classes
- mPrevShareClassloaderProp = System.getProperty(PROP_DEXMAKER_SHARE_CLASSLOADER);
- System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER, "true");
MockitoAnnotations.initMocks(this);
when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
@@ -143,20 +154,22 @@
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
.build();
- mLooper = new TestLooper();
- mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
+ mLooper = TestableLooper.get(this);
+ mHandlerThread = spy(new HandlerThread("TestDhcpServer"));
+ when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
+ mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams,
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
- mServer.start();
- mLooper.dispatchAll();
+ mServer.start(mAssertSuccessCallback);
+ mLooper.processAllMessages();
}
@After
- public void tearDown() {
- // Calling stop() several times is not an issue
- mServer.stop();
- System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER,
- (mPrevShareClassloaderProp == null ? "" : mPrevShareClassloaderProp));
+ public void tearDown() throws Exception {
+ mServer.stop(mAssertSuccessCallback);
+ mLooper.processMessages(1);
+ verify(mPacketListener, times(1)).stop();
+ verify(mHandlerThread, times(1)).quitSafely();
}
@Test
@@ -165,13 +178,6 @@
}
@Test
- public void testStop() throws Exception {
- mServer.stop();
- mLooper.dispatchAll();
- verify(mPacketListener, times(1)).stop();
- }
-
- @Test
public void testDiscover() throws Exception {
// TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
similarity index 96%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
index 2ab2246..3ca0564 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,6 +16,7 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkUtils.inet4AddressToIntHTH;
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
@@ -23,8 +24,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static java.net.InetAddress.parseNumericAddress;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkAddress;
@@ -195,6 +194,11 @@
assertEquals(7, numFields);
}
+ @Test(expected = InvalidParameterException.class)
+ public void testFromParcelableObject_NullArgument() throws InvalidParameterException {
+ DhcpServingParams.fromParcelableObject(null);
+ }
+
private static int[] toIntArray(Collection<Inet4Address> addrs) {
return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
}
diff --git a/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
new file mode 100644
index 0000000..07ad3123
--- /dev/null
+++ b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.android.server.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.util.SharedLog;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SharedLogTest {
+ private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
+ private static final String TIMESTAMP = "HH:MM:SS";
+
+ @Test
+ public void testBasicOperation() {
+ final SharedLog logTop = new SharedLog("top");
+ logTop.mark("first post!");
+
+ final SharedLog logLevel2a = logTop.forSubComponent("twoA");
+ final SharedLog logLevel2b = logTop.forSubComponent("twoB");
+ logLevel2b.e("2b or not 2b");
+ logLevel2b.e("No exception", null);
+ logLevel2b.e("Wait, here's one", new Exception("Test"));
+ logLevel2a.w("second post?");
+
+ final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
+ logTop.log("still logging");
+ logLevel3.log("3 >> 2");
+ logLevel2a.mark("ok: last post");
+
+ final String[] expected = {
+ " - MARK first post!",
+ " - [twoB] ERROR 2b or not 2b",
+ " - [twoB] ERROR No exception",
+ // No stacktrace in shared log, only in logcat
+ " - [twoB] ERROR Wait, here's one: Test",
+ " - [twoA] WARN second post?",
+ " - still logging",
+ " - [twoA.three] 3 >> 2",
+ " - [twoA] MARK ok: last post",
+ };
+ // Verify the logs are all there and in the correct order.
+ verifyLogLines(expected, logTop);
+
+ // In fact, because they all share the same underlying LocalLog,
+ // every subcomponent SharedLog's dump() is identical.
+ verifyLogLines(expected, logLevel2a);
+ verifyLogLines(expected, logLevel2b);
+ verifyLogLines(expected, logLevel3);
+ }
+
+ private static void verifyLogLines(String[] expected, SharedLog log) {
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+ final PrintWriter pw = new PrintWriter(ostream, true);
+ log.dump(null, pw, null);
+
+ final String dumpOutput = ostream.toString();
+ assertTrue(dumpOutput != null);
+ assertTrue(!"".equals(dumpOutput));
+
+ final String[] lines = dumpOutput.split("\n");
+ assertEquals(expected.length, lines.length);
+
+ for (int i = 0; i < expected.length; i++) {
+ String got = lines[i];
+ String want = expected[i];
+ assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
+ assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
+ got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 6abe76a..595aeb3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,5 +1,7 @@
package com.android.settingslib;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+
import android.annotation.ColorInt;
import android.content.Context;
import android.content.Intent;
@@ -429,12 +431,14 @@
// and do not support voice service, and on these SIM cards, we
// want to show signal bars for data service as well as the "no
// service" or "emergency calls only" text that indicates that voice
- // is not available.
+ // is not available. Note that we ignore the IWLAN service state
+ // because that state indicates the use of VoWIFI and not cell service
int state = serviceState.getState();
int dataState = serviceState.getDataRegState();
if (state == ServiceState.STATE_OUT_OF_SERVICE
|| state == ServiceState.STATE_EMERGENCY_ONLY) {
- if (dataState == ServiceState.STATE_IN_SERVICE) {
+ if (dataState == ServiceState.STATE_IN_SERVICE
+ && serviceState.getDataNetworkType() != RIL_RADIO_TECHNOLOGY_IWLAN) {
return ServiceState.STATE_IN_SERVICE;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 594d767..86f0438 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -247,6 +247,15 @@
}
@Test
+ public void isInService_voiceOutOfServiceDataInServiceOnIwLan_returnFalse() {
+ when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mServiceState.getDataNetworkType())
+ .thenReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+ when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ assertThat(Utils.isInService(mServiceState)).isFalse();
+ }
+
+ @Test
public void isInService_voiceOutOfServiceDataOutOfService_returnFalse() {
when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 4e0cbe0..ed18dc7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,14 +24,13 @@
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:background="@android:color/transparent"
android:baselineAligned="false"
- android:clickable="true"
+ android:clickable="false"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
android:paddingStart="0dp"
- android:elevation="4dp"
- android:importantForAccessibility="no" >
+ android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index e63f88a..e030e40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -284,17 +284,10 @@
public void updateEverything() {
post(() -> {
updateVisibilities();
- updateClickabilities();
setClickable(false);
});
}
- private void updateClickabilities() {
- mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
- mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
- mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
- }
-
private void updateVisibilities() {
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7224599..40c6039 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -426,6 +426,7 @@
if (mExpanded == expanded) return;
mExpanded = expanded;
mHeaderQsPanel.setExpanded(expanded);
+ updateEverything();
}
/**
@@ -697,6 +698,10 @@
.start();
}
+ public void updateEverything() {
+ post(() -> setClickable(false));
+ }
+
public void setQSPanel(final QSPanel qsPanel) {
mQsPanel = qsPanel;
setupHost(qsPanel.getHost());
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b01117c..d7a2365 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -503,26 +503,46 @@
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
- // Alarm receivers for scheduled backups & initialization operations
- BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+ // Receivers for scheduled backups and transport initialization operations.
+ BroadcastReceiver runBackupReceiver = new RunBackupReceiver(this);
IntentFilter filter = new IntentFilter();
filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, filter,
- android.Manifest.permission.BACKUP, null);
+ context.registerReceiverAsUser(
+ runBackupReceiver,
+ UserHandle.of(userId),
+ filter,
+ android.Manifest.permission.BACKUP,
+ /* scheduler */ null);
- BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+ BroadcastReceiver runInitReceiver = new RunInitializeReceiver(this);
filter = new IntentFilter();
filter.addAction(RUN_INITIALIZE_ACTION);
- context.registerReceiver(mRunInitReceiver, filter,
- android.Manifest.permission.BACKUP, null);
+ context.registerReceiverAsUser(
+ runInitReceiver,
+ UserHandle.of(userId),
+ filter,
+ android.Manifest.permission.BACKUP,
+ /* scheduler */ null);
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+ mRunBackupIntent =
+ PendingIntent.getBroadcastAsUser(
+ context,
+ /* requestCode */ 0,
+ backupIntent,
+ /* flags */ 0,
+ UserHandle.of(userId));
Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+ mRunInitIntent =
+ PendingIntent.getBroadcastAsUser(
+ context,
+ /* requestCode */ 0,
+ initIntent,
+ /* flags */ 0,
+ UserHandle.of(userId));
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 3b87724..d37b106 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -26,62 +26,84 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
import android.os.Message;
import android.util.Slog;
import com.android.server.backup.UserBackupManagerService;
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION}
+ * that runs an immediate backup operation if eligible.
+ */
public class RunBackupReceiver extends BroadcastReceiver {
+ private final UserBackupManagerService mUserBackupManagerService;
- private UserBackupManagerService backupManagerService;
-
- public RunBackupReceiver(UserBackupManagerService backupManagerService) {
- this.backupManagerService = backupManagerService;
+ public RunBackupReceiver(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
+ /**
+ * Run a backup pass if we're eligible. We're eligible if the following conditions are met:
+ *
+ * <ul>
+ * <li>No transports are pending initialization (otherwise we kick off an initialization
+ * operation instead).
+ * <li>Backup is enabled for the user.
+ * <li>The user has completed setup.
+ * <li>No backup operation is currently running for the user.
+ * </ul>
+ */
public void onReceive(Context context, Intent intent) {
- if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
- synchronized (backupManagerService.getQueueLock()) {
- if (backupManagerService.getPendingInits().size() > 0) {
- // If there are pending init operations, we process those
- // and then settle into the usual periodic backup schedule.
- if (MORE_DEBUG) {
- Slog.v(TAG, "Init pending at scheduled backup");
- }
- try {
- backupManagerService.getAlarmManager().cancel(
- backupManagerService.getRunInitIntent());
- backupManagerService.getRunInitIntent().send();
- } catch (PendingIntent.CanceledException ce) {
- Slog.e(TAG, "Run init intent cancelled");
- // can't really do more than bail here
- }
- } else {
- // Don't run backups now if we're disabled or not yet
- // fully set up.
- if (backupManagerService.isEnabled()
- && backupManagerService.isSetupComplete()) {
- if (!backupManagerService.isBackupRunning()) {
- if (DEBUG) {
- Slog.v(TAG, "Running a backup pass");
- }
+ if (!RUN_BACKUP_ACTION.equals(intent.getAction())) {
+ return;
+ }
- // Acquire the wakelock and pass it to the backup thread. it will
- // be released once backup concludes.
- backupManagerService.setBackupRunning(true);
- backupManagerService.getWakelock().acquire();
-
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
- MSG_RUN_BACKUP);
- backupManagerService.getBackupHandler().sendMessage(msg);
- } else {
- Slog.i(TAG, "Backup time but one already running");
- }
- } else {
- Slog.w(TAG, "Backup pass but enabled=" + backupManagerService.isEnabled()
- + " setupComplete=" + backupManagerService.isSetupComplete());
- }
+ synchronized (mUserBackupManagerService.getQueueLock()) {
+ if (mUserBackupManagerService.getPendingInits().size() > 0) {
+ // If there are pending init operations, we process those and then settle into the
+ // usual periodic backup schedule.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Init pending at scheduled backup");
}
+ try {
+ PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent();
+ mUserBackupManagerService.getAlarmManager().cancel(runInitIntent);
+ runInitIntent.send();
+ } catch (PendingIntent.CanceledException ce) {
+ Slog.w(TAG, "Run init intent cancelled");
+ }
+ } else {
+ // Don't run backups if we're disabled or not yet set up.
+ if (!mUserBackupManagerService.isEnabled()
+ || !mUserBackupManagerService.isSetupComplete()) {
+ Slog.w(
+ TAG,
+ "Backup pass but enabled="
+ + mUserBackupManagerService.isEnabled()
+ + " setupComplete="
+ + mUserBackupManagerService.isSetupComplete());
+ return;
+ }
+
+ // Don't run backups if one is already running.
+ if (mUserBackupManagerService.isBackupRunning()) {
+ Slog.i(TAG, "Backup time but one already running");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Running a backup pass");
+ }
+
+ // Acquire the wakelock and pass it to the backup thread. It will be released once
+ // backup concludes.
+ mUserBackupManagerService.setBackupRunning(true);
+ mUserBackupManagerService.getWakelock().acquire();
+
+ Handler backupHandler = mUserBackupManagerService.getBackupHandler();
+ Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP);
+ backupHandler.sendMessage(message);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 38870cb..97711e3 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -24,41 +24,50 @@
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
-import android.util.ArraySet;
import android.util.Slog;
import com.android.server.backup.UserBackupManagerService;
-public class RunInitializeReceiver extends BroadcastReceiver {
- private final UserBackupManagerService mBackupManagerService;
+import java.util.Set;
- public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
- mBackupManagerService = backupManagerService;
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_INITIALIZE_ACTION}
+ * that runs an initialization operation on all pending transports.
+ */
+public class RunInitializeReceiver extends BroadcastReceiver {
+ private final UserBackupManagerService mUserBackupManagerService;
+
+ public RunInitializeReceiver(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
public void onReceive(Context context, Intent intent) {
- if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
- synchronized (mBackupManagerService.getQueueLock()) {
- final ArraySet<String> pendingInits = mBackupManagerService.getPendingInits();
- if (DEBUG) {
- Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
- }
+ if (!RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
+ return;
+ }
- if (pendingInits.size() > 0) {
- final String[] transports =
- pendingInits.toArray(new String[pendingInits.size()]);
+ synchronized (mUserBackupManagerService.getQueueLock()) {
+ Set<String> pendingInits = mUserBackupManagerService.getPendingInits();
+ if (DEBUG) {
+ Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
+ }
- mBackupManagerService.clearPendingInits();
+ if (pendingInits.size() > 0) {
+ String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
- PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
- wakelock.acquire();
- OnTaskFinishedListener listener = caller -> wakelock.release();
+ mUserBackupManagerService.clearPendingInits();
- Runnable task =
- new PerformInitializeTask(
- mBackupManagerService, transports, null, listener);
- mBackupManagerService.getBackupHandler().post(task);
- }
+ PowerManager.WakeLock wakelock = mUserBackupManagerService.getWakelock();
+ wakelock.acquire();
+ OnTaskFinishedListener listener = caller -> wakelock.release();
+
+ Runnable task =
+ new PerformInitializeTask(
+ mUserBackupManagerService,
+ transports,
+ /* observer */ null,
+ listener);
+ mUserBackupManagerService.getBackupHandler().post(task);
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9dfdddb..eb5be77 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1837,7 +1837,7 @@
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mDeps.getIpServerDependencies()));
+ mDeps.getIpServerDependencies(mContext)));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 7daf71d..a42efe9 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -60,8 +60,8 @@
/**
* Get dependencies to be used by IpServer.
*/
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies();
+ public IpServer.Dependencies getIpServerDependencies(Context context) {
+ return new IpServer.Dependencies(context);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 5210270..ea01a0b 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -273,6 +273,16 @@
// TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
+ /**
+ * Property to disable muting logic in System Audio Control handling. Default is true.
+ *
+ * <p>True means enabling muting logic.
+ * <p>False means never mute device.
+ */
+ // TODO(OEM): set to true to disable muting.
+ static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
+ "ro.hdmi.property_system_audio_mode_muting_enable";
+
// Set to false to allow playback device to go to suspend mode even
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 6570493..7358eaa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -77,10 +77,12 @@
// TODO(amyjojo) make System Audio Control controllable by users
/*mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+ // TODO(b/80297700): set read-only property in config instead of setting here
+ SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "false");
mAutoDeviceOff = mService.readBooleanSetting(
- Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+ Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
mAutoTvOff = mService.readBooleanSetting(
- Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
+ Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
}
@Override
@@ -425,7 +427,21 @@
if (newSystemAudioMode && port >= 0) {
switchToAudioInput();
}
- // TODO(b/80297700): Mute device when TV terminates the system audio control
+ // Mute device when feature is turned off and unmute device when feature is turned on.
+ // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted.
+ boolean currentMuteStatus =
+ mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+ if (SystemProperties.getBoolean(
+ Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
+ && currentMuteStatus == newSystemAudioMode) {
+ mService.getAudioManager()
+ .adjustStreamVolume(
+ AudioManager.STREAM_MUSIC,
+ newSystemAudioMode
+ ? AudioManager.ADJUST_UNMUTE
+ : AudioManager.ADJUST_MUTE,
+ 0);
+ }
updateAudioManagerForSystemAudio(newSystemAudioMode);
synchronized (mLock) {
if (mSystemAudioActivated != newSystemAudioMode) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3f9d928..2464ca7 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -332,6 +332,44 @@
}
}
+ private static class MaxJobCounts {
+ private final KeyValueListParser.IntValue mTotal;
+ private final KeyValueListParser.IntValue mBg;
+
+ private MaxJobCounts(int totalDefault, String totalKey, int bgDefault, String bgKey) {
+ mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
+ mBg = new KeyValueListParser.IntValue(bgKey, bgDefault);
+ }
+
+ public void parse(KeyValueListParser parser) {
+ mTotal.parse(parser);
+ mBg.parse(parser);
+
+ if (mBg.getValue() > mTotal.getValue()) {
+ mBg.setValue(mTotal.getValue());
+ }
+
+ }
+
+ public int getTotalMax() {
+ return mTotal.getValue();
+ }
+
+ public int getBgMax() {
+ return mBg.getValue();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ mTotal.dump(pw, prefix);
+ mBg.dump(pw, prefix);
+ }
+
+ public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) {
+ mTotal.dumpProto(proto, tagTotal);
+ mBg.dumpProto(proto, tagBg);
+ }
+ }
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -492,6 +530,43 @@
* memory state.
*/
int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
+
+ // Max job counts for screen on / off, for each memory trim level.
+ // TODO Remove the old configs such as FG_JOB_COUNT and BG_*_COUNT, once the code switches
+ // to the below configs.
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts(
+ 4, "max_job_total_on_normal",
+ 2, "max_job_bg_on_normal");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts(
+ 4, "max_job_total_on_moderate",
+ 1, "max_job_bg_on_moderate");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts(
+ 4, "max_job_total_on_low",
+ 1, "max_job_bg_on_low");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts(
+ 2, "max_job_total_on_critical",
+ 1, "max_job_bg_on_critical");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts(
+ 8, "max_job_total_off_normal",
+ 4, "max_job_bg_off_normal");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts(
+ 6, "max_job_total_off_moderate",
+ 4, "max_job_bg_off_moderate");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts(
+ 4, "max_job_total_off_low",
+ 1, "max_job_bg_off_low");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts(
+ 2, "max_job_total_off_critical",
+ 1, "max_job_bg_off_critical");
+
/**
* The maximum number of times we allow a job to have itself rescheduled before
* giving up on it, for standard jobs.
@@ -566,7 +641,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
@@ -574,7 +649,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
@@ -582,7 +657,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
@@ -590,7 +665,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
@@ -653,6 +728,17 @@
if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
+
+ MAX_JOB_COUNTS_ON_NORMAL.parse(mParser);
+ MAX_JOB_COUNTS_ON_MODERATE.parse(mParser);
+ MAX_JOB_COUNTS_ON_LOW.parse(mParser);
+ MAX_JOB_COUNTS_ON_CRITICAL.parse(mParser);
+
+ MAX_JOB_COUNTS_OFF_NORMAL.parse(mParser);
+ MAX_JOB_COUNTS_OFF_MODERATE.parse(mParser);
+ MAX_JOB_COUNTS_OFF_LOW.parse(mParser);
+ MAX_JOB_COUNTS_OFF_CRITICAL.parse(mParser);
+
MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
@@ -717,6 +803,17 @@
pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
+
+ MAX_JOB_COUNTS_ON_NORMAL.dump(pw, "");
+ MAX_JOB_COUNTS_ON_MODERATE.dump(pw, "");
+ MAX_JOB_COUNTS_ON_LOW.dump(pw, "");
+ MAX_JOB_COUNTS_ON_CRITICAL.dump(pw, "");
+
+ MAX_JOB_COUNTS_OFF_NORMAL.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_MODERATE.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_LOW.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_CRITICAL.dump(pw, "");
+
pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
@@ -767,6 +864,9 @@
proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
+
+ // TODO Dump max job counts.
+
proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index e0ae68f..ae697b7 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -2,3 +2,19 @@
name: "services.net",
srcs: ["java/**/*.java"],
}
+
+// TODO: move to networking module with DhcpClient and remove lib
+java_library {
+ name: "dhcp-packet-lib",
+ srcs: [
+ "java/android/net/dhcp/*Packet.java",
+ ]
+}
+
+// TODO: move to networking module with IpNeighborMonitor/ConnectivityPacketTracker and remove lib
+java_library {
+ name: "frameworks-net-shared-utils",
+ srcs: [
+ "java/android/net/util/FdEventsReader.java",
+ ]
+}
\ No newline at end of file
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 6ba7d94..ce8b7e7 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,8 +1,5 @@
package android.net.dhcp;
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
-
import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
@@ -37,6 +34,9 @@
public abstract class DhcpPacket {
protected static final String TAG = "DhcpPacket";
+ // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
+ private static final int IPV4_MIN_MTU = 68;
+
// dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
// CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
// DHCP client timeout.
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 493350d..8b22f68 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -17,20 +17,26 @@
package android.net.ip;
import static android.net.NetworkUtils.numericToInetAddress;
-import static android.net.util.NetworkConstants.asByte;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+import static android.net.util.NetworkConstants.asByte;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
+import android.net.INetworkStackStatusCallback;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkStack;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.DhcpServingParamsParcelExt;
+import android.net.dhcp.IDhcpServer;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
@@ -126,6 +132,10 @@
}
public static class Dependencies {
+ private final Context mContext;
+ public Dependencies(Context context) {
+ mContext = context;
+ }
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
@@ -138,9 +148,12 @@
return NetdService.getInstance();
}
- public DhcpServer makeDhcpServer(Looper looper, String ifName,
- DhcpServingParams params, SharedLog log) {
- return new DhcpServer(looper, ifName, params, log);
+ /**
+ * Create a DhcpServer instance to be used by IpServer.
+ */
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
}
}
@@ -197,7 +210,10 @@
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
- private DhcpServer mDhcpServer;
+
+ // To be accessed only on the handler thread
+ private int mDhcpServerStartIndex = 0;
+ private IDhcpServer mDhcpServer;
private RaParams mLastRaParams;
public IpServer(
@@ -252,35 +268,109 @@
private boolean startIPv4() { return configureIPv4(true); }
+ /**
+ * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
+ * handler.
+ *
+ * <p>Different instances of this class can be created for each call to IDhcpServer methods,
+ * with different implementations of the callback, to differentiate handling of success/error in
+ * each call.
+ */
+ private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
+ @Override
+ public void onStatusAvailable(int statusCode) {
+ getHandler().post(() -> callback(statusCode));
+ }
+
+ public abstract void callback(int statusCode);
+ }
+
+ private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
+ private final int mStartIndex;
+
+ private DhcpServerCallbacksImpl(int startIndex) {
+ mStartIndex = startIndex;
+ }
+
+ @Override
+ public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
+ getHandler().post(() -> {
+ // We are on the handler thread: mDhcpServerStartIndex can be read safely.
+ if (mStartIndex != mDhcpServerStartIndex) {
+ // This start request is obsolete. When the |server| binder token goes out of
+ // scope, the garbage collector will finalize it, which causes the network stack
+ // process garbage collector to collect the server itself.
+ return;
+ }
+
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error obtaining DHCP server: " + statusCode);
+ handleError();
+ return;
+ }
+
+ mDhcpServer = server;
+ try {
+ mDhcpServer.start(new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int startStatusCode) {
+ if (startStatusCode != STATUS_SUCCESS) {
+ mLog.e("Error starting DHCP server: " + startStatusCode);
+ handleError();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ private void handleError() {
+ mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ transitionTo(mInitialState);
+ }
+ }
+
private boolean startDhcp(Inet4Address addr, int prefixLen) {
if (mUsingLegacyDhcp) {
return true;
}
- final DhcpServingParams params;
- try {
- params = new DhcpServingParams.Builder()
- .setDefaultRouters(addr)
- .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
- .setDnsServers(addr)
- .setServerAddr(new LinkAddress(addr, prefixLen))
- .setMetered(true)
- .build();
- // TODO: also advertise link MTU
- } catch (DhcpServingParams.InvalidParameterException e) {
- Log.e(TAG, "Invalid DHCP parameters", e);
- return false;
- }
+ final DhcpServingParamsParcel params;
+ params = new DhcpServingParamsParcelExt()
+ .setDefaultRouters(addr)
+ .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
+ .setDnsServers(addr)
+ .setServerAddr(new LinkAddress(addr, prefixLen))
+ .setMetered(true);
+ // TODO: also advertise link MTU
- mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
- mLog.forSubComponent("DHCP"));
- mDhcpServer.start();
+ mDhcpServerStartIndex++;
+ mDeps.makeDhcpServer(
+ mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
return true;
}
private void stopDhcp() {
+ // Make all previous start requests obsolete so servers are not started later
+ mDhcpServerStartIndex++;
+
if (mDhcpServer != null) {
- mDhcpServer.stop();
- mDhcpServer = null;
+ try {
+ mDhcpServer.stop(new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int statusCode) {
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error stopping DHCP server: " + statusCode);
+ mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ // Not much more we can do here
+ }
+ }
+ });
+ mDhcpServer = null;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 3defe56..c183b81 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -16,9 +16,6 @@
package android.net.util;
-import java.nio.ByteBuffer;
-
-
/**
* Networking protocol constants.
*
@@ -81,8 +78,6 @@
* - https://tools.ietf.org/html/rfc791
*/
public static final int IPV4_HEADER_MIN_LEN = 20;
- public static final int IPV4_MIN_MTU = 68;
- public static final int IPV4_MAX_MTU = 65_535;
public static final int IPV4_IHL_MASK = 0xf;
public static final int IPV4_FLAGS_OFFSET = 6;
public static final int IPV4_FRAGMENT_MASK = 0x1fff;
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 74bc147..8b7b59d 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -32,6 +32,7 @@
*
* All access to class methods other than dump() must be on the same thread.
*
+ * TODO: this is a copy of SharedLog in the NetworkStack. Remove after Tethering is migrated.
* @hide
*/
public class SharedLog {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7e115f0..bdede33 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -34,7 +34,6 @@
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -149,6 +148,7 @@
mAvrPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress);
SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
+ SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true");
}
@Test
@@ -179,7 +179,6 @@
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
- @Ignore("b/80297700")
@Test
public void handleRequestShortAudioDescriptor_featureDisabled() throws Exception {
HdmiCecMessage expectedMessage =
@@ -261,7 +260,6 @@
assertThat(mMusicMute).isFalse();
}
- @Ignore("b/80297700")
@Test
public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
@@ -289,9 +287,10 @@
assertThat(mMusicMute).isTrue();
}
- @Ignore("b/80297700")
@Test
public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
+ mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(false);
+ mHdmiCecLocalDeviceAudioSystem.setAutoTvOff(false);
// Set system audio control on first
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
// Check if standby correctly turns off the feature
@@ -372,7 +371,6 @@
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
- @Ignore("b/80297700")
@Test
public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 893dbe3..61c6b48 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -30,7 +30,7 @@
public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
private static final String LOG_TAG = "CellSignalStrengthLte";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
/**
* Indicates the unknown or undetectable RSSI value in ASU.
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index ef185c5..2271069 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -369,7 +369,7 @@
public int getLevel() {
int level = getPrimary().getLevel();
if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
- log("Invalid Level " + level + ", this=" + this);
+ loge("Invalid Level " + level + ", this=" + this);
return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
return getPrimary().getLevel();
@@ -657,9 +657,16 @@
}
/**
- * log
+ * log warning
*/
private static void log(String s) {
Rlog.w(LOG_TAG, s);
}
+
+ /**
+ * log error
+ */
+ private static void loge(String s) {
+ Rlog.e(LOG_TAG, s);
+ }
}
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
index 9c80764..9c7ca7e 100644
--- a/tests/ActivityViewTest/Android.mk
+++ b/tests/ActivityViewTest/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ActivityViewTest
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
index de54cc9..0be1ea0 100644
--- a/tests/ActivityViewTest/AndroidManifest.xml
+++ b/tests/ActivityViewTest/AndroidManifest.xml
@@ -35,17 +35,20 @@
<activity android:name=".ActivityViewActivity"
android:label="AV"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden|adjustResize">
</activity>
<activity android:name=".ActivityViewResizeActivity"
android:label="AV Resize"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden|adjustResize">
</activity>
<activity android:name=".ActivityViewScrollActivity"
android:label="AV Scroll"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden">
</activity>
<activity android:name=".ActivityViewTestActivity"
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
index f7ec562..338d68a 100644
--- a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -15,6 +15,7 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/test_activity_root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -60,11 +61,10 @@
android:background="#00000000"
android:gravity="center" />
- <View
- android:id="@+id/touch_intercept_view"
+ <EditText
+ android:id="@+id/test_activity_edittext"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#00000000"
- />
-
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_margin="16dp" />
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
index 0d62786..ba2c764 100644
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -29,26 +29,24 @@
import android.view.ViewTreeObserver;
import android.widget.TextView;
-public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+public class ActivityViewTestActivity extends Activity {
+ private View mRoot;
private TextView mTextView;
private TextView mWidthTextView;
private TextView mHeightTextView;
private TextView mTouchStateTextView;
- private View mTouchInterceptView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_test_activity);
-
+ mRoot = findViewById(R.id.test_activity_root);
mTextView = findViewById(R.id.test_activity_title);
mWidthTextView = findViewById(R.id.test_activity_width_text);
mHeightTextView = findViewById(R.id.test_activity_height_text);
mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
- mTouchInterceptView = findViewById(R.id.touch_intercept_view);
- mTouchInterceptView.setOnTouchListener(this);
- ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+ ViewTreeObserver viewTreeObserver = mRoot.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
}
@@ -90,8 +88,8 @@
}
private void updateDimensionTexts() {
- mWidthTextView.setText("" + mTouchInterceptView.getWidth());
- mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+ mWidthTextView.setText("" + mRoot.getWidth());
+ mHeightTextView.setText("" + mRoot.getHeight());
}
private void updateTouchState(MotionEvent event) {
@@ -108,8 +106,8 @@
}
@Override
- public boolean onTouch(View v, MotionEvent event) {
+ public boolean dispatchTouchEvent(MotionEvent event) {
updateTouchState(event);
- return true;
+ return super.dispatchTouchEvent(event);
}
}
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 0178228..c3162af 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -22,20 +22,26 @@
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.ip.IpServer.STATE_AVAILABLE;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -48,8 +54,9 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
+import android.net.dhcp.IDhcpServerCallbacks;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -82,16 +89,18 @@
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
+
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
@Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
- @Mock private DhcpServer mDhcpServer;
+ @Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpServer.Dependencies mDependencies;
- @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
+ @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -112,8 +121,18 @@
mLooper.dispatchAll();
reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
- when(mDependencies.makeDhcpServer(
- any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
+
+ doAnswer(inv -> {
+ final IDhcpServerCallbacks cb = inv.getArgument(2);
+ new Thread(() -> {
+ try {
+ cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+ }).run();
+ return null;
+ }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
@@ -399,21 +418,20 @@
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+ verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
}
- private void assertDhcpStarted(IpPrefix expectedPrefix) {
- verify(mDependencies, times(1)).makeDhcpServer(
- eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
- verify(mDhcpServer, times(1)).start();
- final DhcpServingParams params = mDhcpParamsCaptor.getValue();
+ private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
+ verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
+ verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
+ final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
// Last address byte is random
- assertTrue(expectedPrefix.contains(params.serverAddr.getAddress()));
- assertEquals(expectedPrefix.getPrefixLength(), params.serverAddr.getPrefixLength());
- assertEquals(1, params.defaultRouters.size());
- assertEquals(params.serverAddr.getAddress(), params.defaultRouters.iterator().next());
- assertEquals(1, params.dnsServers.size());
- assertEquals(params.serverAddr.getAddress(), params.dnsServers.iterator().next());
+ assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
+ assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
+ assertEquals(1, params.defaultRouters.length);
+ assertEquals(params.serverAddr, params.defaultRouters[0]);
+ assertEquals(1, params.dnsServers.length);
+ assertEquals(params.serverAddr, params.dnsServers[0]);
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
}
@@ -458,7 +476,7 @@
addr4 = addr;
break;
}
- assertTrue("missing IPv4 address", addr4 != null);
+ assertNotNull("missing IPv4 address", addr4);
// Assert the presence of the associated directly connected route.
final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index e6b43d2..1ea83c2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,6 +27,7 @@
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -37,6 +38,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Matchers.anyInt;
@@ -47,6 +49,8 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -74,8 +78,9 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
@@ -86,7 +91,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
-import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -129,6 +133,8 @@
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
+
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -143,9 +149,11 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
- @Mock private DhcpServer mDhcpServer;
+ @Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
+ private final MockIpServerDependencies mIpServerDependencies =
+ spy(new MockIpServerDependencies());
private final MockTetheringDependencies mTetheringDependencies =
new MockTetheringDependencies();
@@ -185,6 +193,47 @@
}
}
+ public class MockIpServerDependencies extends IpServer.Dependencies {
+ MockIpServerDependencies() {
+ super(null);
+ }
+
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+ InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ assertTrue("Non-mocked interface " + ifName,
+ ifName.equals(TEST_USB_IFNAME)
+ || ifName.equals(TEST_WLAN_IFNAME)
+ || ifName.equals(TEST_MOBILE_IFNAME));
+ final String[] ifaces = new String[] {
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
+
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+
+ @Override
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ new Thread(() -> {
+ try {
+ cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+ }).run();
+ }
+ }
+
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine upstreamNetworkMonitorMasterSM;
ArrayList<IpServer> ipv6CoordinatorNotifyList;
@@ -216,35 +265,8 @@
}
@Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
- InterfaceParams ifParams) {
- return mRouterAdvertisementDaemon;
- }
-
- @Override
- public InterfaceParams getInterfaceParams(String ifName) {
- final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
- final int index = ArrayUtils.indexOf(ifaces, ifName);
- assertTrue("Non-mocked interface: " + ifName, index >= 0);
- return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
- MacAddress.ALL_ZEROS_ADDRESS);
- }
-
- @Override
- public INetd getNetdService() {
- return mNetd;
- }
-
- @Override
- public DhcpServer makeDhcpServer(Looper looper, String ifName,
- DhcpServingParams params, SharedLog log) {
- return mDhcpServer;
- }
- };
+ public IpServer.Dependencies getIpServerDependencies(Context context) {
+ return mIpServerDependencies;
}
@Override
@@ -546,7 +568,7 @@
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
}
@Test
@@ -557,7 +579,7 @@
runUsbTethering(upstreamState);
sendIPv6TetherUpdates(upstreamState);
- verify(mDhcpServer, never()).start();
+ verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
}
@Test
@@ -581,7 +603,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -595,7 +617,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
@@ -612,7 +634,7 @@
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
@@ -636,7 +658,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// DHCP not restarted on downstream (still times(1))
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
}
@Test
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 11b408f..9968bda 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -775,6 +775,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static native int %s(int code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -820,6 +821,7 @@
}
// Method header (signature)
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static void write(int code");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -900,6 +902,7 @@
if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
}
+ fprintf(out, " * @hide\n");
fprintf(out, " */\n");
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
}
@@ -916,6 +919,7 @@
field->name.c_str());
for (map<int, string>::const_iterator value = field->enumValues.begin();
value != field->enumValues.end(); value++) {
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static final int %s__%s__%s = %d;\n",
make_constant_name(atom->message).c_str(),
make_constant_name(field->name).c_str(),