Merge "SystemMediaRoute2Provider: Set callback after instantiation is done" into rvc-dev
diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java
deleted file mode 100644
index 83e57e0..0000000
--- a/core/java/android/net/DnsPacket.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.text.TextUtils;
-
-import com.android.internal.util.BitUtils;
-
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.text.DecimalFormat;
-import java.text.FieldPosition;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Defines basic data for DNS protocol based on RFC 1035.
- * Subclasses create the specific format used in DNS packet.
- *
- * @hide
- */
-public abstract class DnsPacket {
- public class DnsHeader {
- private static final String TAG = "DnsHeader";
- public final int id;
- public final int flags;
- public final int rcode;
- private final int[] mRecordCount;
-
- /**
- * Create a new DnsHeader from a positioned ByteBuffer.
- *
- * The ByteBuffer must be in network byte order (which is the default).
- * Reads the passed ByteBuffer from its current position and decodes a DNS header.
- * When this constructor returns, the reading position of the ByteBuffer has been
- * advanced to the end of the DNS header record.
- * This is meant to chain with other methods reading a DNS response in sequence.
- */
- DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
- id = BitUtils.uint16(buf.getShort());
- flags = BitUtils.uint16(buf.getShort());
- rcode = flags & 0xF;
- mRecordCount = new int[NUM_SECTIONS];
- for (int i = 0; i < NUM_SECTIONS; ++i) {
- mRecordCount[i] = BitUtils.uint16(buf.getShort());
- }
- }
-
- /**
- * Get record count by type.
- */
- public int getRecordCount(int type) {
- return mRecordCount[type];
- }
- }
-
- /**
- * Superclass for DNS questions and DNS resource records.
- *
- * DNS questions (No TTL/RDATA)
- * DNS resource records (With TTL/RDATA)
- */
- public class DnsRecord {
- private static final int MAXNAMESIZE = 255;
- private static final int MAXLABELSIZE = 63;
- private static final int MAXLABELCOUNT = 128;
- private static final int NAME_NORMAL = 0;
- private static final int NAME_COMPRESSION = 0xC0;
- private final DecimalFormat byteFormat = new DecimalFormat();
- private final FieldPosition pos = new FieldPosition(0);
-
- private static final String TAG = "DnsRecord";
-
- public final String dName;
- public final int nsType;
- public final int nsClass;
- public final long ttl;
- private final byte[] mRdata;
-
- /**
- * Create a new DnsRecord from a positioned ByteBuffer.
- *
- * Reads the passed ByteBuffer from its current position and decodes a DNS record.
- * When this constructor returns, the reading position of the ByteBuffer has been
- * advanced to the end of the DNS header record.
- * This is meant to chain with other methods reading a DNS response in sequence.
- *
- * @param ByteBuffer input of record, must be in network byte order
- * (which is the default).
- */
- DnsRecord(int recordType, @NonNull ByteBuffer buf)
- throws BufferUnderflowException, ParseException {
- dName = parseName(buf, 0 /* Parse depth */);
- if (dName.length() > MAXNAMESIZE) {
- throw new ParseException(
- "Parse name fail, name size is too long: " + dName.length());
- }
- nsType = BitUtils.uint16(buf.getShort());
- nsClass = BitUtils.uint16(buf.getShort());
-
- if (recordType != QDSECTION) {
- ttl = BitUtils.uint32(buf.getInt());
- final int length = BitUtils.uint16(buf.getShort());
- mRdata = new byte[length];
- buf.get(mRdata);
- } else {
- ttl = 0;
- mRdata = null;
- }
- }
-
- /**
- * Get a copy of rdata.
- */
- @Nullable
- public byte[] getRR() {
- return (mRdata == null) ? null : mRdata.clone();
- }
-
- /**
- * Convert label from {@code byte[]} to {@code String}
- *
- * Follows the same conversion rules of the native code (ns_name.c in libc)
- */
- private String labelToString(@NonNull byte[] label) {
- final StringBuffer sb = new StringBuffer();
- for (int i = 0; i < label.length; ++i) {
- int b = BitUtils.uint8(label[i]);
- // Control characters and non-ASCII characters.
- if (b <= 0x20 || b >= 0x7f) {
- // Append the byte as an escaped decimal number, e.g., "\19" for 0x13.
- sb.append('\\');
- byteFormat.format(b, sb, pos);
- } else if (b == '"' || b == '.' || b == ';' || b == '\\'
- || b == '(' || b == ')' || b == '@' || b == '$') {
- // Append the byte as an escaped character, e.g., "\:" for 0x3a.
- sb.append('\\');
- sb.append((char) b);
- } else {
- // Append the byte as a character, e.g., "a" for 0x61.
- sb.append((char) b);
- }
- }
- return sb.toString();
- }
-
- private String parseName(@NonNull ByteBuffer buf, int depth) throws
- BufferUnderflowException, ParseException {
- if (depth > MAXLABELCOUNT) {
- throw new ParseException("Failed to parse name, too many labels");
- }
- final int len = BitUtils.uint8(buf.get());
- final int mask = len & NAME_COMPRESSION;
- if (0 == len) {
- return "";
- } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
- throw new ParseException("Parse name fail, bad label type");
- } else if (mask == NAME_COMPRESSION) {
- // Name compression based on RFC 1035 - 4.1.4 Message compression
- final int offset = ((len & ~NAME_COMPRESSION) << 8) + BitUtils.uint8(buf.get());
- final int oldPos = buf.position();
- if (offset >= oldPos - 2) {
- throw new ParseException("Parse compression name fail, invalid compression");
- }
- buf.position(offset);
- final String pointed = parseName(buf, depth + 1);
- buf.position(oldPos);
- return pointed;
- } else {
- final byte[] label = new byte[len];
- buf.get(label);
- final String head = labelToString(label);
- if (head.length() > MAXLABELSIZE) {
- throw new ParseException("Parse name fail, invalid label length");
- }
- final String tail = parseName(buf, depth + 1);
- return TextUtils.isEmpty(tail) ? head : head + "." + tail;
- }
- }
- }
-
- public static final int QDSECTION = 0;
- public static final int ANSECTION = 1;
- public static final int NSSECTION = 2;
- public static final int ARSECTION = 3;
- private static final int NUM_SECTIONS = ARSECTION + 1;
-
- private static final String TAG = DnsPacket.class.getSimpleName();
-
- protected final DnsHeader mHeader;
- protected final List<DnsRecord>[] mRecords;
-
- protected DnsPacket(@NonNull byte[] data) throws ParseException {
- if (null == data) throw new ParseException("Parse header failed, null input data");
- final ByteBuffer buffer;
- try {
- buffer = ByteBuffer.wrap(data);
- mHeader = new DnsHeader(buffer);
- } catch (BufferUnderflowException e) {
- throw new ParseException("Parse Header fail, bad input data", e);
- }
-
- mRecords = new ArrayList[NUM_SECTIONS];
-
- for (int i = 0; i < NUM_SECTIONS; ++i) {
- final int count = mHeader.getRecordCount(i);
- if (count > 0) {
- mRecords[i] = new ArrayList(count);
- }
- for (int j = 0; j < count; ++j) {
- try {
- mRecords[i].add(new DnsRecord(i, buffer));
- } catch (BufferUnderflowException e) {
- throw new ParseException("Parse record fail", e);
- }
- }
- }
- }
-}
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 0b1a845..3f7660f 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -38,6 +38,8 @@
import android.system.ErrnoException;
import android.util.Log;
+import com.android.net.module.util.DnsPacket;
+
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -97,7 +99,7 @@
@interface DnsError {}
/**
* Indicates that there was an error parsing the response the query.
- * The cause of this error is available via getCause() and is a ParseException.
+ * The cause of this error is available via getCause() and is a {@link ParseException}.
*/
public static final int ERROR_PARSE = 0;
/**
@@ -290,8 +292,15 @@
}
try {
mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
- } catch (ParseException e) {
- mDnsException = new DnsException(ERROR_PARSE, e);
+ } catch (DnsPacket.ParseException e) {
+ // Convert the com.android.net.module.util.DnsPacket.ParseException to an
+ // android.net.ParseException. This is the type that was used in Q and is implied
+ // by the public documentation of ERROR_PARSE.
+ //
+ // DnsPacket cannot throw android.net.ParseException directly because it's @hide.
+ ParseException pe = new ParseException(e.reason, e.getCause());
+ pe.setStackTrace(e.getStackTrace());
+ mDnsException = new DnsException(ERROR_PARSE, pe);
}
maybeReportAnswer();
}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 3ea443b..8790fb2 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -64,7 +64,7 @@
public static final String SERVICE_INTERFACE =
"android.service.autofill.InlineSuggestionRenderService";
- private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper(), null, true);
private IInlineSuggestionUiCallback mCallback;
@@ -192,15 +192,22 @@
}
return true;
});
+ final InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mMainHandler);
+ mActiveInlineSuggestions.put(uiImpl, true);
- try {
- InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mHandler);
- mActiveInlineSuggestions.put(uiImpl, true);
- callback.onContent(new InlineSuggestionUiWrapper(uiImpl), host.getSurfacePackage(),
- measuredSize.getWidth(), measuredSize.getHeight());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onContent()");
- }
+ // We post the callback invocation to the end of the main thread handler queue, to make
+ // sure the callback happens after the views are drawn. This is needed because calling
+ // {@link SurfaceControlViewHost#setView()} will post a task to the main thread
+ // to draw the view asynchronously.
+ mMainHandler.post(() -> {
+ try {
+ callback.onContent(new InlineSuggestionUiWrapper(uiImpl),
+ host.getSurfacePackage(),
+ measuredSize.getWidth(), measuredSize.getHeight());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent()");
+ }
+ });
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
}
@@ -305,7 +312,7 @@
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
@NonNull InlinePresentation presentation, int width, int height,
@Nullable IBinder hostInputToken, int displayId) {
- mHandler.sendMessage(
+ mMainHandler.sendMessage(
obtainMessage(InlineSuggestionRenderService::handleRenderSuggestion,
InlineSuggestionRenderService.this, callback, presentation,
width, height, hostInputToken, displayId));
@@ -313,7 +320,7 @@
@Override
public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(
+ mMainHandler.sendMessage(obtainMessage(
InlineSuggestionRenderService::handleGetInlineSuggestionsRendererInfo,
InlineSuggestionRenderService.this, callback));
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f944dd7..0d420c5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1184,7 +1184,8 @@
// may have been destroyed so now we need to make
// sure it is re-created.
doOffsetsChanged(false);
- updateSurface(false, false, false);
+ // force relayout to get new surface
+ updateSurface(true, false, false);
}
onVisibilityChanged(visible);
}
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index ab0cc30..2393036 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -210,7 +210,7 @@
if (mInstance != 0) {
native_replySimple(mInstance, unique, FUSE_OK);
}
- mBytesMap.stopUsing(entry.getThreadId());
+ mBytesMap.stopUsing(inode);
recycleLocked(args);
}
break;
@@ -270,7 +270,7 @@
if (mInstance != 0) {
native_replyOpen(mInstance, unique, /* fh */ inode);
entry.opened = true;
- return mBytesMap.startUsing(entry.getThreadId());
+ return mBytesMap.startUsing(inode);
}
} catch (ErrnoException error) {
replySimpleLocked(unique, getError(error));
@@ -354,27 +354,27 @@
}
/**
- * Map between Thread ID and byte buffer.
+ * Map between inode and byte buffer.
*/
private static class BytesMap {
final Map<Long, BytesMapEntry> mEntries = new HashMap<>();
- byte[] startUsing(long threadId) {
- BytesMapEntry entry = mEntries.get(threadId);
+ byte[] startUsing(long inode) {
+ BytesMapEntry entry = mEntries.get(inode);
if (entry == null) {
entry = new BytesMapEntry();
- mEntries.put(threadId, entry);
+ mEntries.put(inode, entry);
}
entry.counter++;
return entry.bytes;
}
- void stopUsing(long threadId) {
- final BytesMapEntry entry = mEntries.get(threadId);
+ void stopUsing(long inode) {
+ final BytesMapEntry entry = mEntries.get(inode);
Objects.requireNonNull(entry);
entry.counter--;
if (entry.counter <= 0) {
- mEntries.remove(threadId);
+ mEntries.remove(inode);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 297d92e..640475e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -51,6 +51,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
@@ -1065,10 +1066,8 @@
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
final Intent intent = bubble.getSettingsIntent(mContext);
collapseStack(() -> {
-
mContext.startActivityAsUser(intent, bubble.getUser());
- logBubbleClickEvent(
- bubble,
+ logBubbleEvent(bubble,
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
});
}
@@ -2757,16 +2756,28 @@
/**
* Logs the bubble UI event.
*
- * @param bubble the bubble that is being interacted on. Null value indicates that
- * the user interaction is not specific to one bubble.
+ * @param provider the bubble view provider that is being interacted on. Null value indicates
+ * that the user interaction is not specific to one bubble.
* @param action the user interaction enum.
*/
- private void logBubbleEvent(@Nullable BubbleViewProvider bubble, int action) {
- if (bubble == null) {
+ private void logBubbleEvent(@Nullable BubbleViewProvider provider, int action) {
+ if (provider == null || provider.getKey().equals(BubbleOverflow.KEY)) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ mContext.getApplicationInfo().packageName,
+ provider == null ? null : BubbleOverflow.KEY /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition(),
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
return;
}
- bubble.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
- getNormalizedYPosition(), getBubbleIndex(bubble));
+ provider.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
+ getNormalizedYPosition(), getBubbleIndex(provider));
}
/**
@@ -2805,20 +2816,4 @@
}
return bubbles;
}
-
- /**
- * Logs bubble UI click event.
- *
- * @param bubble the bubble notification entry that user is interacting with.
- * @param action the user interaction enum.
- */
- private void logBubbleClickEvent(Bubble bubble, int action) {
- bubble.logUIEvent(
- getBubbleCount(),
- action,
- getNormalizedXPosition(),
- getNormalizedYPosition(),
- getBubbleIndex(getExpandedBubble())
- );
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 4821d8c..e83b159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -822,7 +822,7 @@
*/
public void updateSlippery() {
setSlippery(!isQuickStepSwipeUpEnabled() ||
- (mPanelView.isFullyExpanded() && !mPanelView.isCollapsing()));
+ (mPanelView != null && mPanelView.isFullyExpanded() && !mPanelView.isCollapsing()));
}
private void setSlippery(boolean slippery) {
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index e90a2cc..8f072e4 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -15,3 +15,6 @@
rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
+
+# Classes from net-utils-framework-common
+rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index 398ece4..19248ca 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -45,6 +45,8 @@
private final Object mLock;
@NonNull
private final Handler mHandler;
+ @NonNull
+ private final InlineFillUi.InlineUiEventCallback mUiCallback;
@Nullable
@GuardedBy("mLock")
@@ -54,12 +56,14 @@
private InlineFillUi mInlineFillUi;
AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal,
- int userId, ComponentName componentName, Handler handler, Object lock) {
+ int userId, ComponentName componentName, Handler handler, Object lock,
+ InlineFillUi.InlineUiEventCallback callback) {
mInputMethodManagerInternal = inputMethodManagerInternal;
mUserId = userId;
mComponentName = componentName;
mHandler = handler;
mLock = lock;
+ mUiCallback = callback;
}
@@ -82,7 +86,8 @@
// TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
// same field.
mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
- mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras);
+ mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras,
+ mUiCallback);
mSession.onCreateInlineSuggestionsRequestLocked();
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 48895ad..68eeb0a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.util.Slog;
import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -40,6 +41,7 @@
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.ref.WeakReference;
+import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
@@ -65,6 +67,8 @@
private final Handler mHandler;
@NonNull
private final Bundle mUiExtras;
+ @NonNull
+ private final InlineFillUi.InlineUiEventCallback mUiCallback;
@GuardedBy("mLock")
@NonNull
@@ -97,18 +101,22 @@
@GuardedBy("mLock")
private boolean mDestroyed = false;
+ @GuardedBy("mLock")
+ private boolean mPreviousHasNonPinSuggestionShow;
AutofillInlineSuggestionsRequestSession(
@NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
@NonNull ComponentName componentName, @NonNull Handler handler, @NonNull Object lock,
@NonNull AutofillId autofillId,
- @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras,
+ @NonNull InlineFillUi.InlineUiEventCallback callback) {
mInputMethodManagerInternal = inputMethodManagerInternal;
mUserId = userId;
mComponentName = componentName;
mHandler = handler;
mLock = lock;
mUiExtras = uiExtras;
+ mUiCallback = callback;
mAutofillId = autofillId;
mImeRequestConsumer = requestConsumer;
@@ -217,6 +225,7 @@
// No-op if both the previous response and current response are empty.
return;
}
+ maybeNotifyFillUiEventLocked(response.getInlineSuggestions());
updateResponseToImeUncheckLocked(response);
mPreviousResponseIsNotEmpty = !isEmptyResponse;
}
@@ -238,6 +247,38 @@
}
}
+ @GuardedBy("mLock")
+ private void maybeNotifyFillUiEventLocked(@NonNull List<InlineSuggestion> suggestions) {
+ if (mDestroyed) {
+ return;
+ }
+ boolean hasSuggestionToShow = false;
+ for (int i = 0; i < suggestions.size(); i++) {
+ InlineSuggestion suggestion = suggestions.get(i);
+ // It is possible we don't have any match result but we still have pinned
+ // suggestions. Only notify we have non-pinned suggestions to show
+ if (!suggestion.getInfo().isPinned()) {
+ hasSuggestionToShow = true;
+ break;
+ }
+ }
+ if (sDebug) {
+ Slog.d(TAG, "maybeNotifyFillUiEventLoked(): hasSuggestionToShow=" + hasSuggestionToShow
+ + ", mPreviousHasNonPinSuggestionShow=" + mPreviousHasNonPinSuggestionShow);
+ }
+ // Use mPreviousHasNonPinSuggestionShow to save previous status, if the display status
+ // change, we can notify the event.
+ if (hasSuggestionToShow && !mPreviousHasNonPinSuggestionShow) {
+ // From no suggestion to has suggestions to show
+ mUiCallback.notifyInlineUiShown(mAutofillId);
+ } else if (!hasSuggestionToShow && mPreviousHasNonPinSuggestionShow) {
+ // From has suggestions to no suggestions to show
+ mUiCallback.notifyInlineUiHidden(mAutofillId);
+ }
+ // Update the latest status
+ mPreviousHasNonPinSuggestionShow = hasSuggestionToShow;
+ }
+
/**
* Handles the {@code request} and {@code callback} received from the IME.
*
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 712b413..3114a6a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -843,7 +843,18 @@
setClientLocked(client);
mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
- userId, componentName, handler, mLock);
+ userId, componentName, handler, mLock,
+ new InlineFillUi.InlineUiEventCallback() {
+ @Override
+ public void notifyInlineUiShown(AutofillId autofillId) {
+ notifyFillUiShown(autofillId);
+ }
+
+ @Override
+ public void notifyInlineUiHidden(AutofillId autofillId) {
+ notifyFillUiHidden(autofillId);
+ }
+ });
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -1329,6 +1340,26 @@
this, intentSender, intent));
}
+ private void notifyFillUiHidden(@NonNull AutofillId autofillId) {
+ synchronized (mLock) {
+ try {
+ mClient.notifyFillUiHidden(this.id, autofillId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending fill UI hidden notification", e);
+ }
+ }
+ }
+
+ private void notifyFillUiShown(@NonNull AutofillId autofillId) {
+ synchronized (mLock) {
+ try {
+ mClient.notifyFillUiShown(this.id, autofillId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending fill UI shown notification", e);
+ }
+ }
+ }
+
private void doStartIntentSender(IntentSender intentSender, Intent intent) {
try {
synchronized (mLock) {
@@ -2769,11 +2800,6 @@
// TODO(b/156099633): remove this once framework gets out of business of resending
// inline suggestions when IME visibility changes.
mInlineSessionController.hideInlineSuggestionsUiLocked(viewState.id);
- try {
- mClient.notifyFillUiHidden(this.id, viewState.id);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error requesting to hide fill UI", e);
- }
viewState.resetState(ViewState.STATE_CHANGED);
return;
} else if ((viewState.id.equals(this.mCurrentViewId))
@@ -2800,11 +2826,6 @@
// TODO: we should be able to replace this with controller#filterInlineFillUiLocked
// to accomplish filtering for augmented autofill.
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
- try {
- mClient.notifyFillUiHidden(this.id, mCurrentViewId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending fill UI hidden notification", e);
- }
}
}
@@ -2890,11 +2911,6 @@
if (requestShowInlineSuggestionsLocked(response, filterText)) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN);
- try {
- mClient.notifyFillUiShown(this.id, mCurrentViewId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending fill UI shown notification", e);
- }
//TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
// rather than here where framework sends back the response.
mService.logDatasetShown(id, mClientState);
@@ -2965,11 +2981,6 @@
synchronized (mLock) {
mInlineSessionController.hideInlineSuggestionsUiLocked(
focusedId);
- try {
- mClient.notifyFillUiHidden(this.id, focusedId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending fill UI hidden notification", e);
- }
}
}, remoteRenderService);
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
@@ -3484,11 +3495,6 @@
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
- try {
- mClient.notifyFillUiHidden(this.id, mCurrentViewId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending fill UI hidden notification", e);
- }
}
autoFillApp(dataset);
return;
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index 1c430b3..627c073 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -297,4 +297,19 @@
*/
void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent);
}
+
+ /**
+ * Callback for inline suggestion Ui related events.
+ */
+ public interface InlineUiEventCallback {
+ /**
+ * Callback to notify inline ui is shown.
+ */
+ void notifyInlineUiShown(@NonNull AutofillId autofillId);
+
+ /**
+ * Callback to notify inline ui is hidden.
+ */
+ void notifyInlineUiHidden(@NonNull AutofillId autofillId);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
index 7075608..71f1dbf 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
@@ -1,10 +1,10 @@
package com.android.server.backup.restore;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import libcore.io.IoUtils;
-import java.io.FileInputStream;
import java.io.InputStream;
class FullRestoreEngineThread implements Runnable {
@@ -19,7 +19,7 @@
// We *do* want this FileInputStream to own the underlying fd, so that
// when we are finished with it, it closes this end of the pipe in a way
// that signals its other end.
- mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
+ mEngineStream = new AutoCloseInputStream(engineSocket);
// Tell it to be sure to leave the agent instance up after finishing
mMustKillAgent = false;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 63a984c..d479f23 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -52,7 +52,6 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.storage.StorageUserConnection.REMOTE_TIMEOUT_SECONDS;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -224,6 +223,9 @@
private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
+ // How long we wait to reset storage, if we failed to call onMount on the
+ // external storage service.
+ public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
/**
* If {@code 1}, enables the isolated storage feature. If {@code -1},
* disables the isolated storage feature. If {@code 0}, uses the default
@@ -2202,7 +2204,7 @@
} catch (ExternalStorageServiceException e) {
Slog.e(TAG, "Failed to mount volume " + vol, e);
- int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
+ int nextResetSeconds = FAILED_MOUNT_RESET_TIMEOUT_SECONDS;
Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
mHandler.removeMessages(H_RESET);
mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index cd6b98d..9203122 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -655,7 +655,7 @@
final int appxUidCount = userCount * allSettings.size();
for (int su = 0; su < userCount; su++) {
int subjectUser = allUsers[su].id;
- for (int ou = su; ou < userCount; ou++) {
+ for (int ou = 0; ou < userCount; ou++) {
int otherUser = allUsers[ou].id;
int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
if (!mShouldFilterCache.contains(subjectUid)) {
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 9d6a647..b00540f 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -56,6 +56,15 @@
public ParcelFileDescriptor addBridge(MountScope mountScope)
throws FuseUnavailableMountException, NativeDaemonConnectorException {
+ /*
+ ** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc)
+ **
+ ** (Thread A) Got Java lock (addBrdige) -> Try to get Native lock (native_add_brdige)
+ ** (Thread B) Got Native lock (FuseBrdigeLoop.start) -> Try to get Java lock (onClosed)
+ **
+ ** Guarantee the lock order (native lock -> java lock) when adding Bridge.
+ */
+ native_lock();
try {
synchronized (this) {
Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0);
@@ -73,6 +82,7 @@
return result;
}
} finally {
+ native_unlock();
IoUtils.closeQuietly(mountScope);
}
}
@@ -159,4 +169,6 @@
private native void native_delete(long loop);
private native void native_start_loop(long loop);
private native int native_add_bridge(long loop, int mountId, int deviceId);
+ private native void native_lock();
+ private native void native_unlock();
}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 94a2502..ed57067 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -62,7 +62,7 @@
public final class StorageUserConnection {
private static final String TAG = "StorageUserConnection";
- public static final int REMOTE_TIMEOUT_SECONDS = 5;
+ public static final int REMOTE_TIMEOUT_SECONDS = 20;
private final Object mLock = new Object();
private final Context mContext;
@@ -202,6 +202,7 @@
try {
if (!latch.await(REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
// TODO(b/140025078): Call ActivityManager ANR API?
+ Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
throw new TimeoutException("Latch wait for " + reason + " elapsed");
}
} catch (InterruptedException e) {
diff --git a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
index e519633..20210ab 100644
--- a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
+++ b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp
@@ -123,6 +123,14 @@
return proxyFd[1].release();
}
+void com_android_server_storage_AppFuseBridge_lock(JNIEnv* env, jobject self) {
+ fuse::FuseBridgeLoop::Lock();
+}
+
+void com_android_server_storage_AppFuseBridge_unlock(JNIEnv* env, jobject self) {
+ fuse::FuseBridgeLoop::Unlock();
+}
+
const JNINativeMethod methods[] = {
{
"native_new",
@@ -143,6 +151,16 @@
"native_add_bridge",
"(JII)I",
reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_add_bridge)
+ },
+ {
+ "native_lock",
+ "()V",
+ reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_lock)
+ },
+ {
+ "native_unlock",
+ "()V",
+ reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_unlock)
}
};
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
index ff2236d..4f092b9 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
@@ -227,6 +227,23 @@
assertEquivalent(client, deser);
}
+ @Test(expected = XmlPullParserException.class)
+ public void testReadEmptyFile_ThrowException() throws XmlPullParserException, IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+ serializer.setOutput(output, Encoding.UTF_8.name());
+ // create empty xml document
+ serializer.startDocument(null, true);
+ serializer.endDocument();
+ serializer.flush();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setInput(input, Encoding.UTF_8.name());
+ SliceClientPermissions.createFrom(parser, mock(DirtyTracker.class));
+ // Should throw exception since the xml is empty
+ }
+
private void assertEquivalent(SliceClientPermissions o1, SliceClientPermissions o2) {
assertEquals(o1.getPkg(), o2.getPkg());
ArrayList<SliceAuthority> a1 = new ArrayList<>(o1.getAuthorities());
diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java
deleted file mode 100644
index 975abf4..0000000
--- a/tests/net/java/android/net/DnsPacketTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DnsPacketTest {
- private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag,
- int qCount, int aCount, int nsCount, int arCount) {
- assertEquals(header.id, id);
- assertEquals(header.flags, flag);
- assertEquals(header.getRecordCount(DnsPacket.QDSECTION), qCount);
- assertEquals(header.getRecordCount(DnsPacket.ANSECTION), aCount);
- assertEquals(header.getRecordCount(DnsPacket.NSSECTION), nsCount);
- assertEquals(header.getRecordCount(DnsPacket.ARSECTION), arCount);
- }
-
- private void assertRecordParses(DnsPacket.DnsRecord record, String dname,
- int dtype, int dclass, int ttl, byte[] rr) {
- assertEquals(record.dName, dname);
- assertEquals(record.nsType, dtype);
- assertEquals(record.nsClass, dclass);
- assertEquals(record.ttl, ttl);
- assertTrue(Arrays.equals(record.getRR(), rr));
- }
-
- class TestDnsPacket extends DnsPacket {
- TestDnsPacket(byte[] data) throws ParseException {
- super(data);
- }
-
- public DnsHeader getHeader() {
- return mHeader;
- }
- public List<DnsRecord> getRecordList(int secType) {
- return mRecords[secType];
- }
- }
-
- @Test
- public void testNullDisallowed() {
- try {
- new TestDnsPacket(null);
- fail("Exception not thrown for null byte array");
- } catch (ParseException e) {
- }
- }
-
- @Test
- public void testV4Answer() throws Exception {
- final byte[] v4blob = new byte[] {
- /* Header */
- 0x55, 0x66, /* Transaction ID */
- (byte) 0x81, (byte) 0x80, /* Flags */
- 0x00, 0x01, /* Questions */
- 0x00, 0x01, /* Answer RRs */
- 0x00, 0x00, /* Authority RRs */
- 0x00, 0x00, /* Additional RRs */
- /* Queries */
- 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
- 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
- 0x00, 0x01, /* Type */
- 0x00, 0x01, /* Class */
- /* Answers */
- (byte) 0xc0, 0x0c, /* Name */
- 0x00, 0x01, /* Type */
- 0x00, 0x01, /* Class */
- 0x00, 0x00, 0x01, 0x2b, /* TTL */
- 0x00, 0x04, /* Data length */
- (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
- };
- TestDnsPacket packet = new TestDnsPacket(v4blob);
-
- // Header part
- assertHeaderParses(packet.getHeader(), 0x5566, 0x8180, 1, 1, 0, 0);
-
- // Record part
- List<DnsPacket.DnsRecord> qdRecordList =
- packet.getRecordList(DnsPacket.QDSECTION);
- assertEquals(qdRecordList.size(), 1);
- assertRecordParses(qdRecordList.get(0), "www.google.com", 1, 1, 0, null);
-
- List<DnsPacket.DnsRecord> anRecordList =
- packet.getRecordList(DnsPacket.ANSECTION);
- assertEquals(anRecordList.size(), 1);
- assertRecordParses(anRecordList.get(0), "www.google.com", 1, 1, 0x12b,
- new byte[]{ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 });
- }
-
- @Test
- public void testV6Answer() throws Exception {
- final byte[] v6blob = new byte[] {
- /* Header */
- 0x77, 0x22, /* Transaction ID */
- (byte) 0x81, (byte) 0x80, /* Flags */
- 0x00, 0x01, /* Questions */
- 0x00, 0x01, /* Answer RRs */
- 0x00, 0x00, /* Authority RRs */
- 0x00, 0x00, /* Additional RRs */
- /* Queries */
- 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
- 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
- 0x00, 0x1c, /* Type */
- 0x00, 0x01, /* Class */
- /* Answers */
- (byte) 0xc0, 0x0c, /* Name */
- 0x00, 0x1c, /* Type */
- 0x00, 0x01, /* Class */
- 0x00, 0x00, 0x00, 0x37, /* TTL */
- 0x00, 0x10, /* Data length */
- 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 /* Address */
- };
- TestDnsPacket packet = new TestDnsPacket(v6blob);
-
- // Header part
- assertHeaderParses(packet.getHeader(), 0x7722, 0x8180, 1, 1, 0, 0);
-
- // Record part
- List<DnsPacket.DnsRecord> qdRecordList =
- packet.getRecordList(DnsPacket.QDSECTION);
- assertEquals(qdRecordList.size(), 1);
- assertRecordParses(qdRecordList.get(0), "www.google.com", 28, 1, 0, null);
-
- List<DnsPacket.DnsRecord> anRecordList =
- packet.getRecordList(DnsPacket.ANSECTION);
- assertEquals(anRecordList.size(), 1);
- assertRecordParses(anRecordList.get(0), "www.google.com", 28, 1, 0x37,
- new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 });
- }
-}