diff options
65 files changed, 1825 insertions, 646 deletions
diff --git a/api/current.txt b/api/current.txt index 77cb9e490b04..477e346b26a3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24653,7 +24653,7 @@ package android.media.tv { public final class TvInputInfo implements android.os.Parcelable { method public boolean canRecord(); - method public android.content.Intent createSettingsIntent(); + method public deprecated android.content.Intent createSettingsIntent(); method public android.content.Intent createSetupIntent(); method public int describeContents(); method public android.os.Bundle getExtras(); @@ -33878,6 +33878,7 @@ package android.provider { field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; + field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type"; field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype"; @@ -39812,7 +39813,6 @@ package android.telephony { method public android.os.PersistableBundle getCarrierConfig(); method public android.telephony.CellLocation getCellLocation(); method public int getDataActivity(); - method public boolean getDataEnabled(); method public int getDataNetworkType(); method public int getDataState(); method public java.lang.String getDeviceId(); @@ -39853,6 +39853,7 @@ package android.telephony { method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); method public boolean isSmsCapable(); diff --git a/api/system-current.txt b/api/system-current.txt index 827b46ce7746..04dc632313e3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -26653,7 +26653,7 @@ package android.media.tv { public final class TvInputInfo implements android.os.Parcelable { method public boolean canRecord(); - method public android.content.Intent createSettingsIntent(); + method public deprecated android.content.Intent createSettingsIntent(); method public android.content.Intent createSetupIntent(); method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; @@ -36758,6 +36758,7 @@ package android.provider { field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; + field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type"; field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype"; @@ -43216,8 +43217,8 @@ package android.telephony { method public int getCurrentPhoneType(); method public int getCurrentPhoneType(int); method public int getDataActivity(); - method public boolean getDataEnabled(); - method public boolean getDataEnabled(int); + method public deprecated boolean getDataEnabled(); + method public deprecated boolean getDataEnabled(int); method public int getDataNetworkType(); method public int getDataState(); method public java.lang.String getDeviceId(); @@ -43264,6 +43265,7 @@ package android.telephony { method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isConcurrentVoiceAndDataAllowed(); method public boolean isDataConnectivityPossible(); + method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isIdle(); method public boolean isNetworkRoaming(); diff --git a/api/test-current.txt b/api/test-current.txt index 04f4f21e095f..6962c45b26f6 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -24754,7 +24754,7 @@ package android.media.tv { public final class TvInputInfo implements android.os.Parcelable { method public boolean canRecord(); - method public android.content.Intent createSettingsIntent(); + method public deprecated android.content.Intent createSettingsIntent(); method public android.content.Intent createSetupIntent(); method public int describeContents(); method public android.os.Bundle getExtras(); @@ -34005,6 +34005,7 @@ package android.provider { field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME"; field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI"; + field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH"; field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT"; field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type"; field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype"; @@ -40001,7 +40002,6 @@ package android.telephony { method public android.os.PersistableBundle getCarrierConfig(); method public android.telephony.CellLocation getCellLocation(); method public int getDataActivity(); - method public boolean getDataEnabled(); method public int getDataNetworkType(); method public int getDataState(); method public java.lang.String getDeviceId(); @@ -40042,6 +40042,7 @@ package android.telephony { method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String); method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isConcurrentVoiceAndDataAllowed(); + method public boolean isDataEnabled(); method public boolean isHearingAidCompatibilitySupported(); method public boolean isNetworkRoaming(); method public boolean isSmsCapable(); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index be38f42490e5..77c4c7eea811 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -597,8 +597,7 @@ public final class LoadedApk { // Avoid the binder call when the package is the current application package. // The activity manager will perform ensure that dexopt is performed before // spinning up the process. - if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) { - VMRuntime.getRuntime().vmInstructionSet(); + if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) { try { ActivityThread.getPackageManager().notifyPackageUse(mPackageName, PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index d428a3a857b7..2f87633a39d3 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -24,6 +24,7 @@ import android.database.Cursor; import android.database.CursorToBulkCursorAdaptor; import android.database.DatabaseUtils; import android.database.IContentObserver; +import android.database.PageViewCursor; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -103,6 +104,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr if (cursor != null) { CursorToBulkCursorAdaptor adaptor = null; + cursor = PageViewCursor.wrap(cursor, queryArgs); try { adaptor = new CursorToBulkCursorAdaptor(cursor, observer, getProviderName()); diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java new file mode 100644 index 000000000000..fbd039d91742 --- /dev/null +++ b/core/java/android/database/PageViewCursor.java @@ -0,0 +1,245 @@ +/* + * 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.database; + +import static com.android.internal.util.Preconditions.checkArgument; + +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.os.Bundle; +import android.util.Log; +import android.util.MathUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +/** + * Cursor wrapper that provides visibility into a subset of a wrapped cursor. + * + * The window is specified by offset and limit. + * + * @hide + */ +public final class PageViewCursor extends CrossProcessCursorWrapper { + + /** + * An extra added to results that are auto-paged using the wrapper. + */ + public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED"; + + private static final String TAG = "PageViewCursor"; + private static final boolean DEBUG = false; + private static final boolean VERBOSE = false; + + private final int mOffset; // aka first index + private final int mCount; + private final Bundle mExtras; + + private int mPos = -1; + + /** + * @see PageViewCursor#wrap(Cursor, Bundle) + */ + @VisibleForTesting + public PageViewCursor(Cursor cursor, int offset, int limit) { + super(cursor); + + checkArgument(offset > -1); + checkArgument(limit > -1); + + mOffset = offset; + + mExtras = new Bundle(); + Bundle extras = cursor.getExtras(); + if (extras != null) { + mExtras.putAll(extras); + } + mExtras.putBoolean(EXTRA_AUTO_PAGED, true); + + // We need a mutable bundle so we can add QUERY_RESULT_SIZE. + // Direct equality check is correct here. Bundle.EMPTY is a specific instance + // of Bundle that is immutable by way of implementation. + // mExtras = (extras == Bundle.EMPTY) ? new Bundle() : extras; + + // When we're wrapping another cursor, it should not already be "paged". + checkArgument(!mExtras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)); + + int count = mCursor.getCount(); + mExtras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, count); + + mCount = MathUtils.constrain(count - offset, 0, limit); + + if (DEBUG) Log.d(TAG, "Wrapped cursor" + + " offset: " + mOffset + + ", limit: " + limit + + ", delegate_size: " + count + + ", paged_count: " + mCount); + } + + @Override + public Bundle getExtras() { + return mExtras; + } + + @Override + public int getPosition() { + return mPos; + } + + @Override + public boolean isBeforeFirst() { + if (mCount == 0) { + return true; + } + return mPos == -1; + } + + @Override + public boolean isAfterLast() { + if (mCount == 0) { + return true; + } + return mPos == mCount; + } + + @Override + public boolean isFirst() { + return mPos == 0; + } + + @Override + public boolean isLast() { + return mPos == mCount - 1; + } + + @Override + public boolean moveToFirst() { + return moveToPosition(0); + } + + @Override + public boolean moveToLast() { + return moveToPosition(mCount - 1); + } + + @Override + public boolean moveToNext() { + return move(1); + } + + @Override + public boolean moveToPrevious() { + return move(-1); + } + + @Override + public boolean move(int offset) { + return moveToPosition(mPos + offset); + } + + @Override + public boolean moveToPosition(int position) { + if (position >= mCount) { + if (VERBOSE) Log.v(TAG, "Invalid Positon: " + position + " >= count: " + mCount + + ". Moving to last record."); + mPos = mCount; + super.moveToPosition(mOffset + mPos); // move into "after last" state. + return false; + } + + // Make sure position isn't before the beginning of the cursor + if (position < 0) { + if (VERBOSE) Log.v(TAG, "Ignoring invalid move to position: " + position); + mPos = -1; + super.moveToPosition(mPos); + return false; + } + + if (position == mPos) { + if (VERBOSE) Log.v(TAG, "Ignoring no-op move to position: " + position); + return true; + } + + int delegatePosition = position + mOffset; + if (VERBOSE) Log.v(TAG, "Moving delegate cursor to position: " + delegatePosition); + if (super.moveToPosition(delegatePosition)) { + mPos = position; + return true; + } else { + mPos = -1; + super.moveToPosition(-1); + return false; + } + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public int getCount() { + return mCount; + } + + /** + * Wraps the cursor such that it will honor paging args (if present), AND if the cursor + * does not report paging size. + * + * <p>No-op if cursor already contains paging or is less than specified page size. + */ + public static Cursor wrap(Cursor cursor, @Nullable Bundle queryArgs) { + + boolean hasPagingArgs = + queryArgs != null + && (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET) + || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT)); + + if (!hasPagingArgs) { + if (VERBOSE) Log.d(TAG, "No-wrap: No paging args in request."); + return cursor; + } + + if (hasPagedResponseDetails(cursor.getExtras())) { + if (VERBOSE) Log.d(TAG, "No-wrap. Cursor has paging details."); + return cursor; + } + + return new PageViewCursor( + cursor, + queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0), + queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE)); + } + + /** + * @return true if the extras contains information indicating the associated + * cursor is paged. + */ + private static boolean hasPagedResponseDetails(@Nullable Bundle extras) { + if (extras != null && extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) { + return true; + } + + String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS); + if (honoredArgs != null && ( + ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET) + || ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) { + return true; + } + + return false; + } +} diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java index 6fdc739a5f54..1b1b958dce9b 100644 --- a/core/java/android/net/ConnectivityMetricsEvent.java +++ b/core/java/android/net/ConnectivityMetricsEvent.java @@ -23,35 +23,24 @@ import android.os.Parcelable; public final class ConnectivityMetricsEvent implements Parcelable { /** The time when this event was collected, as returned by System.currentTimeMillis(). */ - final public long timestamp; - - /** The subsystem that generated the event. One of the COMPONENT_TAG_xxx constants. */ - final public int componentTag; - - /** The subsystem-specific event ID. */ - final public int eventTag; + public long timestamp; /** Opaque event-specific data. */ - final public Parcelable data; + public Parcelable data; + + public ConnectivityMetricsEvent() { + } - public ConnectivityMetricsEvent(long timestamp, int componentTag, - int eventTag, Parcelable data) { - this.timestamp = timestamp; - this.componentTag = componentTag; - this.eventTag = eventTag; - this.data = data; + public ConnectivityMetricsEvent(Parcel in) { + timestamp = in.readLong(); + data = in.readParcelable(null); } /** Implement the Parcelable interface */ public static final Parcelable.Creator<ConnectivityMetricsEvent> CREATOR = new Parcelable.Creator<ConnectivityMetricsEvent> (){ public ConnectivityMetricsEvent createFromParcel(Parcel source) { - final long timestamp = source.readLong(); - final int componentTag = source.readInt(); - final int eventTag = source.readInt(); - final Parcelable data = source.readParcelable(null); - return new ConnectivityMetricsEvent(timestamp, componentTag, - eventTag, data); + return new ConnectivityMetricsEvent(source); } public ConnectivityMetricsEvent[] newArray(int size) { @@ -59,7 +48,6 @@ public final class ConnectivityMetricsEvent implements Parcelable { } }; - /** Implement the Parcelable interface */ @Override public int describeContents() { return 0; @@ -68,13 +56,11 @@ public final class ConnectivityMetricsEvent implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(timestamp); - dest.writeInt(componentTag); - dest.writeInt(eventTag); dest.writeParcelable(data, 0); } + @Override public String toString() { - return String.format("ConnectivityMetricsEvent(%tT.%tL, %d, %d): %s", - timestamp, timestamp, componentTag, eventTag, data); + return String.format("ConnectivityMetricsEvent(%tT.%tL): %s", timestamp, timestamp, data); } } diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index 173e5fd0fbc1..aaad1fa68267 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -74,7 +74,10 @@ public class IpConnectivityLog { } try { - int left = mService.logEvent(new ConnectivityMetricsEvent(timestamp, 0, 0, data)); + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = timestamp; + ev.data = data; + int left = mService.logEvent(ev); return left >= 0; } catch (RemoteException e) { Log.e(TAG, "Error logging event", e); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index a0d16bc2d9b0..dac835463c02 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -44,7 +44,6 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Pair; import android.view.View; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -8913,11 +8912,15 @@ public final class ContactsContract { * ambiguous then the activity should prompt the user for the recipient to send the message * to. * <p> + * Voice Assistant may provide additional information to messaging app about which account + * to use for sending a message by populating {@link #EXTRA_SENDER_ACCOUNT_HASH}. + * <p> * Output: nothing * * @see #EXTRA_RECIPIENT_CONTACT_URI * @see #EXTRA_RECIPIENT_CONTACT_CHAT_ID * @see #EXTRA_RECIPIENT_CONTACT_NAME + * @see #EXTRA_SENDER_ACCOUNT_HASH * @see #METADATA_ACCOUNT_TYPE * @see #METADATA_MIMETYPE */ @@ -8975,6 +8978,16 @@ public final class ContactsContract { "android.provider.extra.RECIPIENT_CONTACT_NAME"; /** + * This optional extra specifies the hash of the account that should be used by messaging + * app for sending voice message with {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS}. The + * value of this extra is a {@code String} and should be the value of {@link + * android.accounts.Account#hashCode()} for some account returned by {@link + * android.accounts.AccountManager#getAccounts()}. + */ + public static final String EXTRA_SENDER_ACCOUNT_HASH = + "android.provider.extra.SENDER_ACCOUNT_HASH"; + + /** * A string associated with an {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} activity * describing {@link RawContacts#ACCOUNT_TYPE} for the corresponding Contacts Provider * implementation. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8a2a14c1f8a1..391ee835e92a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5605,10 +5605,10 @@ public final class Settings { "accessibility_web_content_key_bindings"; /** - * Setting that specifies whether the display magnification is enabled. - * Display magnifications allows the user to zoom in the display content - * and is targeted to low vision users. The current magnification scale - * is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. + * Setting that specifies whether the display magnification is enabled via a system-wide + * triple tap gesture. Display magnifications allows the user to zoom in the display content + * and is targeted to low vision users. The current magnification scale is controlled by + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. * * @hide */ @@ -5616,11 +5616,23 @@ public final class Settings { "accessibility_display_magnification_enabled"; /** + * Setting that specifies whether the display magnification is enabled via a shortcut + * affordance within the system's navigation area. Display magnifications allows the user to + * zoom in the display content and is targeted to low vision users. The current + * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. + * + * @hide + */ + public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = + "accessibility_display_magnification_navbar_enabled"; + + /** * Setting that specifies what the display magnification scale is. * Display magnifications allows the user to zoom in the display * content and is targeted to low vision users. Whether a display * magnification is performed is controlled by - * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} and + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED} * * @hide */ @@ -6950,6 +6962,7 @@ public final class Settings { ACCESSIBILITY_DISPLAY_DALTONIZER, ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, ACCESSIBILITY_SCRIPT_INJECTION, ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index a541a4cd3b1e..6dbc09ceef97 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -168,6 +168,14 @@ public abstract class WindowManagerInternal { public abstract void setMagnificationSpec(MagnificationSpec spec); /** + * Set by the accessibility framework to indicate whether the magnifiable regions of the display + * should be shown. + * + * @param show {@code true} to show magnifiable region bounds, {@code false} to hide + */ + public abstract void setForceShowMagnifiableBounds(boolean show); + + /** * Obtains the magnification regions. * * @param magnificationRegion the current magnification region diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index c95a1fb966f5..06ac8699f864 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -121,8 +121,8 @@ final class TextClassifierImpl implements TextClassifier { .classifyText(text.toString(), startIndex, endIndex); if (results.length > 0) { // TODO: Added this log for debug only. Remove before release. - Log.d(LOG_TAG, - String.format("Classification type: %s", results[0].mCollection)); + Log.d(LOG_TAG, String.format( + "Classification type: %s", getHighestScoringType(results))); return createClassificationResult(results, classified); } } @@ -188,7 +188,7 @@ final class TextClassifierImpl implements TextClassifier { builder.setEntityType(classifications[i].mCollection, classifications[i].mScore); } - final String type = classifications[0].mCollection; + final String type = getHighestScoringType(classifications); final Intent intent = IntentFactory.create(mContext, type, text.toString()); final PackageManager pm; final ResolveInfo resolveInfo; @@ -226,6 +226,23 @@ final class TextClassifierImpl implements TextClassifier { return builder.build(); } + private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) { + if (types.length < 1) { + return ""; + } + + String type = types[0].mCollection; + float highestScore = types[0].mScore; + final int size = types.length; + for (int i = 1; i < size; i++) { + if (types[i].mScore > highestScore) { + type = types[i].mCollection; + highestScore = types[i].mScore; + } + } + return type; + } + /** * @throws IllegalArgumentException if text is null; startIndex is negative; * endIndex is greater than text.length() or is not greater than startIndex @@ -265,7 +282,7 @@ final class TextClassifierImpl implements TextClassifier { final SmartSelection.ClassificationResult[] results = smartSelection.classifyText(text, selectionStart, selectionEnd); if (results.length > 0) { - final String type = results[0].mCollection; + final String type = getHighestScoringType(results); if (matches(type, linkMask)) { final Intent intent = IntentFactory.create( context, type, text.substring(selectionStart, selectionEnd)); diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java index ae31873492e9..95d714f1c3c7 100644 --- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java @@ -321,7 +321,7 @@ public class PipSnapAlgorithm { stackBounds.top)); boundsOut.set(stackBounds); if (mIsMinimized) { - boundsOut.offsetTo(boundedLeft, boundsOut.top); + boundsOut.offsetTo(boundedLeft, boundedTop); return; } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 79b0cd1aced8..818cc2c2421d 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -1163,21 +1163,21 @@ public final class FloatingToolbar { isLastItem && menuItemButtonWidth <= availableWidth - extraPadding; if (canFitWithOverflow || canFitNoOverflow) { if (isNewGroup) { - final View border = createBorder(mContext); - final int borderWidth = border.getLayoutParams().width; + final View divider = createDivider(mContext); + final int dividerWidth = divider.getLayoutParams().width; // Add extra padding to the end of the previous button. // Half of the extra padding (less borderWidth) goes to the previous button. View previousButton = mMainPanel.getChildAt(mMainPanel.getChildCount() - 1); final int prevPaddingEnd = previousButton.getPaddingEnd() - + extraPadding / 2 - borderWidth; + + extraPadding / 2 - dividerWidth; previousButton.setPaddingRelative( previousButton.getPaddingStart(), previousButton.getPaddingTop(), prevPaddingEnd, previousButton.getPaddingBottom()); final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams(); - prevParams.width += extraPadding / 2 - borderWidth; + prevParams.width += extraPadding / 2 - dividerWidth; previousButton.setLayoutParams(prevParams); // Add extra padding to the start of this button. @@ -1190,8 +1190,8 @@ public final class FloatingToolbar { menuItemButton.getPaddingEnd(), menuItemButton.getPaddingBottom()); - // Include a border. - mMainPanel.addView(border); + // Include a divider. + mMainPanel.addView(divider); } setButtonTagAndClickListener(menuItemButton, menuItem); @@ -1670,21 +1670,28 @@ public final class FloatingToolbar { return popupWindow; } - private static View createBorder(Context context) { + private static View createDivider(Context context) { // TODO: Inflate this instead. - View border = new View(context); + View divider = new View(context); + int _1dp = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( _1dp, ViewGroup.LayoutParams.MATCH_PARENT); params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10); - border.setLayoutParams(params); - border.setBackgroundColor(Color.parseColor("#9E9E9E")); - border.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - border.setEnabled(false); - border.setFocusable(false); - border.setContentDescription(null); - return border; + divider.setLayoutParams(params); + + TypedArray a = context.obtainStyledAttributes( + new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor }); + divider.setBackgroundColor(a.getColor(0, 0)); + a.recycle(); + + divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + divider.setEnabled(false); + divider.setFocusable(false); + divider.setContentDescription(null); + + return divider; } /** diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index f85219453212..a8d683028c13 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -453,7 +453,8 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int dst = dstBitmap.getAddr(x, y); SkColorSpace* colorSpace = dstBitmap.colorSpace(); - if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) { + if (dstBitmap.colorType() == kRGBA_F16_SkColorType || + GraphicsJNI::isColorSpaceSRGB(colorSpace)) { // now copy/convert each scanline for (int y = 0; y < height; y++) { proc(dst, src, width, x, y); @@ -1267,7 +1268,8 @@ static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, proc(dst, src, 1, bitmap.getColorTable()); SkColorSpace* colorSpace = bitmap.colorSpace(); - if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) { + if (bitmap.colorType() != kRGBA_F16_SkColorType && + !GraphicsJNI::isColorSpaceSRGB(colorSpace)) { auto sRGB = SkColorSpace::MakeSRGB(); auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get()); xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0], @@ -1299,7 +1301,8 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, SkColor* d = (SkColor*)dst + offset; SkColorSpace* colorSpace = bitmap.colorSpace(); - if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) { + if (bitmap.colorType() == kRGBA_F16_SkColorType || + GraphicsJNI::isColorSpaceSRGB(colorSpace)) { while (--height >= 0) { proc(d, src, width, ctable); d += stride; @@ -1342,7 +1345,8 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, } SkColorSpace* colorSpace = bitmap.colorSpace(); - if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) { + if (bitmap.colorType() != kRGBA_F16_SkColorType && + !GraphicsJNI::isColorSpaceSRGB(colorSpace)) { auto sRGB = SkColorSpace::MakeSRGB(); auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace); xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color, diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 18462376cd20..fa25a8f0c596 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -239,30 +239,38 @@ namespace PaintGlue { return result; } - static jint doTextRunCursor(JNIEnv *env, Paint* paint, const jchar *text, jint start, - jint count, jint flags, jint offset, jint opt) { + static jint doTextRunCursor(JNIEnv *env, Paint* paint, Typeface* typeface, const jchar *text, + jint start, jint count, jint dir, jint offset, jint opt) { minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt); - size_t result = minikin::GraphemeBreak::getTextRunCursor(text, start, count, offset, - moveOpt); + int bidiFlags = dir == 1 ? minikin::kBidi_Force_RTL : minikin::kBidi_Force_LTR; + std::unique_ptr<float[]> advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, + advancesArray.get()); + size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, + start, count, offset, moveOpt); return static_cast<jint>(result); } - static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text, - jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) { + static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, + jlong typefaceHandle, jcharArray text, jint contextStart, jint contextCount, jint dir, + jint offset, jint cursorOpt) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); + Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, nullptr); - jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir, - offset, cursorOpt); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextCount, dir, offset, cursorOpt); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } - static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text, - jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) { + static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, + jlong typefaceHandle, jstring text, jint contextStart, jint contextEnd, jint dir, + jint offset, jint cursorOpt) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); + Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, nullptr); - jint result = doTextRunCursor(env, paint, textArray, contextStart, - contextEnd - contextStart, dir, offset, cursorOpt); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextEnd - contextStart, dir, offset, cursorOpt); env->ReleaseStringChars(text, textArray); return result; } @@ -983,8 +991,8 @@ static const JNINativeMethod methods[] = { {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F", (void*) PaintGlue::getTextAdvances__StringIIIII_FI}, - {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, - {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I", + {"nGetTextRunCursor", "(JJ[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, + {"nGetTextRunCursor", "(JJLjava/lang/String;IIIII)I", (void*) PaintGlue::getTextRunCursor__String}, {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c547ae52b958..4432e3c90f1a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -576,6 +576,7 @@ <attr name="floatingToolbarItemBackgroundDrawable" format="reference" /> <attr name="floatingToolbarOpenDrawable" format="reference" /> <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" /> + <attr name="floatingToolbarDividerColor" format="reference" /> <!-- ============ --> <!-- Alert Dialog styles --> @@ -8379,7 +8380,9 @@ <!-- Component name of an activity that allows the user to set up this service. --> <attr name="setupActivity" format="string" /> <!-- Component name of an activity that allows the user to modify the settings for this - service. --> + service. + {@deprecated This value is deprecated and not used by the framework starting from API + level 26. Use setupActivity instead.} --> <attr name="settingsActivity" /> <!-- Attribute whether the TV input service can record programs. This value can be changed at runtime by calling diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 6015ed5259d8..f9fd57cf5df9 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -194,4 +194,8 @@ <color name="tooltip_background_dark">#e6616161</color> <color name="tooltip_background_light">#e6FFFFFF</color> + + <!-- FloatingToolbar --> + <color name="floating_popup_divider_dark">#2F2F2F</color> + <color name="floating_popup_divider_light">#E9E9E9</color> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0c318cf5e355..26d71c35522d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2425,6 +2425,7 @@ <java-symbol type="drawable" name="ft_avd_toarrow" /> <java-symbol type="drawable" name="ft_avd_toarrow_animation" /> <java-symbol type="drawable" name="ft_avd_tooverflow_animation" /> + <java-symbol type="attr" name="floatingToolbarDividerColor" /> <java-symbol type="string" name="date_picker_prev_month_button" /> <java-symbol type="string" name="date_picker_next_month_button" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index d100c63d4ec1..a661b070872d 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -398,6 +398,7 @@ please see themes_device_defaults.xml. <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item> <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item> + <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item> <!-- SearchView attributes --> <item name="searchViewStyle">@style/Widget.Holo.SearchView</item> @@ -559,6 +560,7 @@ please see themes_device_defaults.xml. <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item> <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item> <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item> + <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item> <!-- Tooltip popup colors --> <item name="tooltipForegroundColor">@color/bright_foreground_dark</item> diff --git a/core/tests/coretests/src/android/database/PageViewCursorTest.java b/core/tests/coretests/src/android/database/PageViewCursorTest.java new file mode 100644 index 000000000000..0be89d5361d8 --- /dev/null +++ b/core/tests/coretests/src/android/database/PageViewCursorTest.java @@ -0,0 +1,318 @@ +/* + * 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.database; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.os.Bundle; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; +import android.util.MathUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; + +@RunWith(AndroidJUnit4.class) +public class PageViewCursorTest { + + private static final int ITEM_COUNT = 20; + + private static final String NAME_COLUMN = "name"; + private static final String NUM_COLUMN = "num"; + + private static final String[] COLUMNS = new String[]{ + NAME_COLUMN, + NUM_COLUMN + }; + + private static final String[] NAMES = new String[] { + "000", + "111", + "222", + "333", + "444", + "555", + "666", + "777", + "888", + "999", + "aaa", + "bbb", + "ccc", + "ddd", + "eee", + "fff", + "ggg", + "hhh", + "iii", + "jjj" + }; + + private MatrixCursor mDelegate; + private PageViewCursor mCursor; + + @Before + public void setUp() { + Random rand = new Random(); + + mDelegate = new MatrixCursor(COLUMNS); + for (int i = 0; i < ITEM_COUNT; i++) { + MatrixCursor.RowBuilder row = mDelegate.newRow(); + row.add(NAME_COLUMN, NAMES[i]); + row.add(NUM_COLUMN, rand.nextInt()); + } + + mCursor = new PageViewCursor(mDelegate, 10, 5); + } + + @Test + public void testPage_Size() { + assertEquals(5, mCursor.getCount()); + } + + @Test + public void testPage_TotalSize() { + assertEquals(ITEM_COUNT, mCursor.getExtras().getInt(ContentResolver.EXTRA_TOTAL_SIZE)); + } + + @Test + public void testPage_OffsetExceedsCursorCount_EffectivelyEmptyCursor() { + mCursor = new PageViewCursor(mDelegate, ITEM_COUNT * 2, 5); + assertEquals(0, mCursor.getCount()); + } + + @Test + public void testMoveToPosition() { + assertTrue(mCursor.moveToPosition(0)); + assertEquals(NAMES[10], mCursor.getString(0)); + assertTrue(mCursor.moveToPosition(1)); + assertEquals(NAMES[11], mCursor.getString(0)); + assertTrue(mCursor.moveToPosition(4)); + assertEquals(NAMES[14], mCursor.getString(0)); + + // and then back down again for good measure. + assertTrue(mCursor.moveToPosition(1)); + assertEquals(NAMES[11], mCursor.getString(0)); + assertTrue(mCursor.moveToPosition(0)); + assertEquals(NAMES[10], mCursor.getString(0)); + } + + @Test + public void testMoveToPosition_MoveToSamePosition_NoOp() { + assertTrue(mCursor.moveToPosition(1)); + assertEquals(NAMES[11], mCursor.getString(0)); + assertTrue(mCursor.moveToPosition(1)); + assertEquals(NAMES[11], mCursor.getString(0)); + } + + @Test + public void testMoveToPosition_PositionOutOfBounds_MovesToBeforeFirst() { + assertTrue(mCursor.moveToPosition(0)); + assertEquals(NAMES[10], mCursor.getString(0)); + + // move before + assertFalse(mCursor.moveToPosition(-12)); + assertTrue(mCursor.isBeforeFirst()); + } + + @Test + public void testMoveToPosition_PositionOutOfBounds_MovesToAfterLast() { + assertTrue(mCursor.moveToPosition(0)); + assertEquals(NAMES[10], mCursor.getString(0)); + + assertFalse(mCursor.moveToPosition(222)); + assertTrue(mCursor.isAfterLast()); + } + + @Test + public void testPosition() { + assertEquals(-1, mCursor.getPosition()); + } + + @Test + public void testIsBeforeFirst() { + assertTrue(mCursor.isBeforeFirst()); + mCursor.moveToFirst(); + assertFalse(mCursor.isBeforeFirst()); + } + + @Test + public void testCount_ZeroForEmptyCursor() { + mCursor = new PageViewCursor(mDelegate, 0, 0); + assertEquals(0, mCursor.getCount()); + } + + @Test + public void testIsBeforeFirst_TrueForEmptyCursor() { + mCursor = new PageViewCursor(mDelegate, 0, 0); + assertTrue(mCursor.isBeforeFirst()); + } + + @Test + public void testIsAfterLast() { + assertFalse(mCursor.isAfterLast()); + mCursor.moveToLast(); + mCursor.moveToNext(); + assertTrue(mCursor.isAfterLast()); + } + + @Test + public void testIsAfterLast_TrueForEmptyCursor() { + mCursor = new PageViewCursor(mDelegate, 0, 0); + assertTrue(mCursor.isAfterLast()); + } + + @Test + public void testIsFirst() { + assertFalse(mCursor.isFirst()); + mCursor.moveToFirst(); + assertTrue(mCursor.isFirst()); + } + + @Test + public void testIsLast() { + assertFalse(mCursor.isLast()); + mCursor.moveToLast(); + assertTrue(mCursor.isLast()); + } + + @Test + public void testMove() { + // note that initial position is -1, so moving + // 2 will only put as at 1. + mCursor.move(2); + assertEquals(NAMES[11], mCursor.getString(0)); + mCursor.move(-1); + assertEquals(NAMES[10], mCursor.getString(0)); + } + + @Test + public void testMoveToFist() { + mCursor.moveToPosition(3); + mCursor.moveToFirst(); + assertEquals(NAMES[10], mCursor.getString(0)); + } + + @Test + public void testMoveToLast() { + mCursor.moveToLast(); + assertEquals(NAMES[14], mCursor.getString(0)); + } + + @Test + public void testMoveToNext() { + // default position is -1, so next is 0. + mCursor.moveToNext(); + assertEquals(NAMES[10], mCursor.getString(0)); + } + + @Test + public void testMoveToNext_AfterLastReturnsFalse() { + mCursor.moveToLast(); + assertFalse(mCursor.moveToNext()); + } + + @Test + public void testMoveToPrevious() { + mCursor.moveToPosition(3); + mCursor.moveToPrevious(); + assertEquals(NAMES[12], mCursor.getString(0)); + } + + @Test + public void testMoveToPrevious_BeforeFirstReturnsFalse() { + assertFalse(mCursor.moveToPrevious()); + } + + @Test + public void testWindow_ReadPastEnd() { + assertFalse(mCursor.moveToPosition(10)); + } + + @Test + public void testOffset_LimitOutOfBounds() { + mCursor = new PageViewCursor(mDelegate, 5, 100); + assertEquals(15, mCursor.getCount()); + } + + @Test + public void testPagingMarker() { + mCursor = new PageViewCursor(mDelegate, 5, 100); + assertTrue(mCursor.getExtras().getBoolean(PageViewCursor.EXTRA_AUTO_PAGED)); + } + + @Test + public void testWrap() { + Bundle queryArgs = new Bundle(); + queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); + queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + assertTrue(wrapped instanceof PageViewCursor); + assertEquals(5, wrapped.getCount()); + } + + @Test + public void testWrap_NoOpWithoutPagingArgs() { + Cursor wrapped = PageViewCursor.wrap(mDelegate, Bundle.EMPTY); + assertTrue(mDelegate == wrapped); + } + + @Test + public void testWrap_NoOpCursorsWithExistingPaging_ByTotalSize() { + Bundle extras = new Bundle(); + extras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, 5); + mDelegate.setExtras(extras); + + Bundle queryArgs = new Bundle(); + queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); + queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + assertTrue(mDelegate == wrapped); + } + + @Test + public void testWrap_NoOpCursorsWithExistingPaging_ByHonoredArgs() { + Bundle extras = new Bundle(); + extras.putStringArray( + ContentResolver.EXTRA_HONORED_ARGS, + new String[] { + ContentResolver.QUERY_ARG_OFFSET, + ContentResolver.QUERY_ARG_LIMIT + }); + mDelegate.setExtras(extras); + + Bundle queryArgs = new Bundle(); + queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5); + queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5); + Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs); + assertTrue(mDelegate == wrapped); + } + + private void assertStringAt(int row, int column, String expected) { + mCursor.moveToPosition(row); + assertEquals(expected, mCursor.getString(column)); + } +} diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 3d5ba7938b31..ed587bbaed24 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -791,12 +791,12 @@ public final class Bitmap implements Parcelable { int neww = width; int newh = height; - Canvas canvas = new Canvas(); Bitmap bitmap; Paint paint; Rect srcR = new Rect(x, y, x + width, y + height); RectF dstR = new RectF(0, 0, width, height); + RectF deviceR = new RectF(); Config newConfig = Config.ARGB_8888; final Config config = source.getConfig(); @@ -827,7 +827,6 @@ public final class Bitmap implements Parcelable { } else { final boolean transformed = !m.rectStaysRect(); - RectF deviceR = new RectF(); m.mapRect(deviceR, dstR); neww = Math.round(deviceR.width()); @@ -841,9 +840,6 @@ public final class Bitmap implements Parcelable { } bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha()); - canvas.translate(-deviceR.left, -deviceR.top); - canvas.concat(m); - paint = new Paint(); paint.setFilterBitmap(filter); if (transformed) { @@ -857,7 +853,9 @@ public final class Bitmap implements Parcelable { bitmap.setHasAlpha(source.hasAlpha()); bitmap.setPremultiplied(source.mRequestPremultiplied); - canvas.setBitmap(bitmap); + Canvas canvas = new Canvas(bitmap); + canvas.translate(-deviceR.left, -deviceR.top); + canvas.concat(m); canvas.drawBitmap(source, srcR, dstR, paint); canvas.setBitmap(null); if (isHardware) { diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index b1d51ec2d92b..7ca4615434f5 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2294,7 +2294,7 @@ public class Paint { throw new IndexOutOfBoundsException(); } - return nGetTextRunCursor(mNativePaint, text, + return nGetTextRunCursor(mNativePaint, mNativeTypeface, text, contextStart, contextLength, dir, offset, cursorOpt); } @@ -2380,7 +2380,7 @@ public class Paint { throw new IndexOutOfBoundsException(); } - return nGetTextRunCursor(mNativePaint, text, + return nGetTextRunCursor(mNativePaint, mNativeTypeface, text, contextStart, contextEnd, dir, offset, cursorOpt); } @@ -2686,9 +2686,9 @@ public class Paint { private static native float nGetTextAdvances(long paintPtr, long typefacePtr, String text, int start, int end, int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex); - private native int nGetTextRunCursor(long paintPtr, char[] text, + private native int nGetTextRunCursor(long paintPtr, long typefacePtr, char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt); - private native int nGetTextRunCursor(long paintPtr, String text, + private native int nGetTextRunCursor(long paintPtr, long typefacePtr, String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt); private static native void nGetTextPath(long paintPtr, long typefacePtr, int bidiFlags, char[] text, int index, int count, float x, float y, long path); diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 39184f1af878..0906ba50f7df 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -677,7 +677,7 @@ public class AudioRecord implements AudioRouting ((audioSource > MediaRecorder.getAudioSourceMax()) && (audioSource != MediaRecorder.AudioSource.RADIO_TUNER) && (audioSource != MediaRecorder.AudioSource.HOTWORD)) ) { - throw new IllegalArgumentException("Invalid audio source."); + throw new IllegalArgumentException("Invalid audio source " + audioSource); } mRecordSource = audioSource; @@ -703,8 +703,8 @@ public class AudioRecord implements AudioRouting mAudioFormat = audioFormat; break; default: - throw new IllegalArgumentException("Unsupported sample encoding." - + " Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT."); + throw new IllegalArgumentException("Unsupported sample encoding " + audioFormat + + ". Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT."); } } @@ -722,7 +722,8 @@ public class AudioRecord implements AudioRouting int frameSizeInBytes = mChannelCount * (AudioFormat.getBytesPerSample(mAudioFormat)); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { - throw new IllegalArgumentException("Invalid audio buffer size."); + throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize + + " (frame size " + frameSizeInBytes + ")"); } mNativeBufferSizeInBytes = audioBufferSize; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 35988d40fe5d..a292b8e4f81e 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -138,7 +138,6 @@ public final class TvInputInfo implements Parcelable { // Attributes from XML meta data. private final String mSetupActivity; - private final String mSettingsActivity; private final boolean mCanRecord; private final int mTunerCount; @@ -259,9 +258,8 @@ public final class TvInputInfo implements Parcelable { private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput, CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected, - String setupActivity, String settingsActivity, boolean canRecord, int tunerCount, - HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId, - Bundle extras) { + String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo, + boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) { mService = service; mId = id; mType = type; @@ -272,7 +270,6 @@ public final class TvInputInfo implements Parcelable { mIconStandby = iconStandby; mIconDisconnected = iconDisconnected; mSetupActivity = setupActivity; - mSettingsActivity = settingsActivity; mCanRecord = canRecord; mTunerCount = tunerCount; mHdmiDeviceInfo = hdmiDeviceInfo; @@ -340,14 +337,12 @@ public final class TvInputInfo implements Parcelable { /** * Returns an intent to start the settings activity for this TV input. + * + * @deprecated Use {@link #createSetupIntent()} instead. Settings activity is deprecated. + * Use setup activity instead to provide settings. */ + @Deprecated public Intent createSettingsIntent() { - if (!TextUtils.isEmpty(mSettingsActivity)) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity); - intent.putExtra(EXTRA_INPUT_ID, getId()); - return intent; - } return null; } @@ -554,7 +549,6 @@ public final class TvInputInfo implements Parcelable { && Objects.equals(mIconStandby, obj.mIconStandby) && Objects.equals(mIconDisconnected, obj.mIconDisconnected) && TextUtils.equals(mSetupActivity, obj.mSetupActivity) - && TextUtils.equals(mSettingsActivity, obj.mSettingsActivity) && mCanRecord == obj.mCanRecord && mTunerCount == obj.mTunerCount && Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo) @@ -589,7 +583,6 @@ public final class TvInputInfo implements Parcelable { dest.writeParcelable(mIconStandby, flags); dest.writeParcelable(mIconDisconnected, flags); dest.writeString(mSetupActivity); - dest.writeString(mSettingsActivity); dest.writeByte(mCanRecord ? (byte) 1 : 0); dest.writeInt(mTunerCount); dest.writeParcelable(mHdmiDeviceInfo, flags); @@ -631,7 +624,6 @@ public final class TvInputInfo implements Parcelable { mIconStandby = in.readParcelable(null); mIconDisconnected = in.readParcelable(null); mSetupActivity = in.readString(); - mSettingsActivity = in.readString(); mCanRecord = in.readByte() == 1; mTunerCount = in.readInt(); mHdmiDeviceInfo = in.readParcelable(null); @@ -678,7 +670,6 @@ public final class TvInputInfo implements Parcelable { private Icon mIconStandby; private Icon mIconDisconnected; private String mSetupActivity; - private String mSettingsActivity; private Boolean mCanRecord; private Integer mTunerCount; private TvInputHardwareInfo mTvInputHardwareInfo; @@ -906,7 +897,7 @@ public final class TvInputInfo implements Parcelable { } parseServiceMetadata(type); return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId, - mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity, + mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount, mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras); } @@ -961,8 +952,6 @@ public final class TvInputInfo implements Parcelable { if (inputType == TYPE_TUNER && TextUtils.isEmpty(mSetupActivity)) { throw new IllegalStateException("Setup activity not found for " + si.name); } - mSettingsActivity = sa.getString( - com.android.internal.R.styleable.TvInputService_settingsActivity); if (mCanRecord == null) { mCanRecord = sa.getBoolean( com.android.internal.R.styleable.TvInputService_canRecord, false); diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h index 803ed29bdf33..9b57958c4473 100644 --- a/media/mca/filterfw/jni/jni_util.h +++ b/media/mca/filterfw/jni/jni_util.h @@ -198,7 +198,8 @@ class ObjectPool { CObjMap objects_; FlagMap owns_; - DISALLOW_COPY_AND_ASSIGN(ObjectPool); + ObjectPool(const ObjectPool&) = delete; + ObjectPool& operator=(const ObjectPool&) = delete; }; template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL; diff --git a/media/mca/filterfw/native/Android.mk b/media/mca/filterfw/native/Android.mk index 2e900fe29e38..feaefcbad6c5 100644 --- a/media/mca/filterfw/native/Android.mk +++ b/media/mca/filterfw/native/Android.mk @@ -41,7 +41,11 @@ LOCAL_EXPORT_LDLIBS := -llog -lgcc LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -LOCAL_STATIC_LIBRARIES := libarect +LOCAL_STATIC_LIBRARIES := \ + libarect \ + +LOCAL_SHARED_LIBRARIES += \ + libgui \ # TODO: Build a shared library as well? include $(BUILD_STATIC_LIBRARY) diff --git a/media/mca/filterfw/native/base/utilities.h b/media/mca/filterfw/native/base/utilities.h index 6bb3b7f7c9cf..2818f7222dd3 100644 --- a/media/mca/filterfw/native/base/utilities.h +++ b/media/mca/filterfw/native/base/utilities.h @@ -23,18 +23,6 @@ namespace android { namespace filterfw { -// Convenience Macro to make copy constructor and assignment operator private -// (thereby disallowing copying and assigning). -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DISALLOW_COPY_AND_ASSIGN(TypeName) - // STLDeleteContainerPointers() // For a range within a container of pointers, calls delete // (non-array version) on these pointers. diff --git a/media/mca/filterfw/native/core/gl_env.h b/media/mca/filterfw/native/core/gl_env.h index 04453016dc93..6af91af6f6c1 100644 --- a/media/mca/filterfw/native/core/gl_env.h +++ b/media/mca/filterfw/native/core/gl_env.h @@ -256,7 +256,8 @@ class GLEnv { std::map<int, ShaderProgram*> attached_shaders_; std::map<int, VertexFrame*> attached_vframes_; - DISALLOW_COPY_AND_ASSIGN(GLEnv); + GLEnv(const GLEnv&) = delete; + GLEnv& operator=(const GLEnv&) = delete; }; } // namespace filterfw diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h index 2da557dec000..fd5216542910 100644 --- a/media/mca/filterfw/native/core/native_frame.h +++ b/media/mca/filterfw/native/core/native_frame.h @@ -76,7 +76,8 @@ class NativeFrame { // Capacity of data buffer in bytes. int capacity_; - DISALLOW_COPY_AND_ASSIGN(NativeFrame); + NativeFrame(const NativeFrame&) = delete; + NativeFrame& operator=(const NativeFrame&) = delete; }; } // namespace filterfw diff --git a/media/mca/filterfw/native/core/native_program.h b/media/mca/filterfw/native/core/native_program.h index ce704af67aa8..e39afc9931ae 100644 --- a/media/mca/filterfw/native/core/native_program.h +++ b/media/mca/filterfw/native/core/native_program.h @@ -75,7 +75,8 @@ class NativeProgram { // Pointer to user data void* user_data_; - DISALLOW_COPY_AND_ASSIGN(NativeProgram); + NativeProgram(const NativeProgram&) = delete; + NativeProgram& operator=(const NativeProgram&) = delete; }; } // namespace filterfw diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml index 2e642ec63cac..8df194c11800 100644 --- a/packages/CarrierDefaultApp/AndroidManifest.xml +++ b/packages/CarrierDefaultApp/AndroidManifest.xml @@ -25,7 +25,6 @@ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" /> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> <application android:label="@string/app_name" > @@ -34,10 +33,16 @@ <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" /> </intent-filter> </receiver> - <activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity" - android:theme="@android:style/Theme.Translucent.NoTitleBar" - android:excludeFromRecents="true"/> <service android:name="com.android.carrierdefaultapp.ProvisionObserver" android:permission="android.permission.BIND_JOB_SERVICE"/> + <activity + android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity" + android:label="@string/action_bar_label" + android:theme="@style/AppTheme" + android:configChanges="keyboardHidden|orientation|screenSize" > + <intent-filter> + <category android:name="android.intent.category.DEFAULT"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png Binary files differnew file mode 100644 index 000000000000..08294cee4587 --- /dev/null +++ b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png diff --git a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml index dc54fe2a3fac..75aa40522a8e 100644 --- a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml +++ b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml @@ -22,4 +22,4 @@ <path android:fillColor="#757575" android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/> -</vector>
\ No newline at end of file +</vector> diff --git a/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml new file mode 100644 index 000000000000..528576b57e5a --- /dev/null +++ b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml @@ -0,0 +1,34 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="com.android.carrierdefaultapp.CaptivePortalLoginActivity" + tools:ignore="MergeRootFrame"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <TextView + android:id="@+id/url_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="20sp" + android:singleLine="true" /> + + <ProgressBar + android:id="@+id/progress_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="?android:attr/progressBarStyleHorizontal" /> + + <WebView + android:id="@+id/webview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentBottom="false" + android:layout_alignParentRight="false" /> + +</LinearLayout> +</FrameLayout> diff --git a/packages/CarrierDefaultApp/res/values/dimens.xml b/packages/CarrierDefaultApp/res/values/dimens.xml index a3c5049bfd8b..1ea8c351293d 100644 --- a/packages/CarrierDefaultApp/res/values/dimens.xml +++ b/packages/CarrierDefaultApp/res/values/dimens.xml @@ -1,3 +1,6 @@ <resources> <dimen name="glif_icon_size">32dp</dimen> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> </resources> diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index f904600df772..f9342b7c5899 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -6,9 +6,8 @@ <string name="no_data_notification_id">Your mobile data has been deactivated</string> <string name="portal_notification_detail">Tap to visit the %s website</string> <string name="no_data_notification_detail">Please contact your service provider %s</string> - <string name="progress_dialogue_network_connection">Connecting to captive portal...</string> - <string name="alert_dialogue_network_timeout">Network timeout, would you like to retry?</string> - <string name="alert_dialogue_network_timeout_title">Network unavailable</string> - <string name="quit">Quit</string> - <string name="wait">Wait</string> + <string name="action_bar_label">Sign in to mobile network</string> + <string name="ssl_error_warning">The network you’re trying to join has security issues.</string> + <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string> + <string name="ssl_error_continue">Continue anyway via browser</string> </resources> diff --git a/packages/CarrierDefaultApp/res/values/styles.xml b/packages/CarrierDefaultApp/res/values/styles.xml index 3d2691505f56..939c1aa4c5da 100644 --- a/packages/CarrierDefaultApp/res/values/styles.xml +++ b/packages/CarrierDefaultApp/res/values/styles.xml @@ -1,3 +1,16 @@ <resources> - <style name="AlertDialog" parent="android:Theme.Material.Light.Dialog.Alert"/> + <style name="AppBaseTheme" parent="@android:style/Theme.Material.Settings"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + <!-- Setting's theme's accent color makes ProgressBar useless, reset back. --> + <item name="android:colorAccent">@*android:color/material_deep_teal_500</item> + </style> </resources> diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java deleted file mode 100644 index 28251cb394e7..000000000000 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2016 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.carrierdefaultapp; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.CaptivePortal; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkRequest; -import android.os.Bundle; -import android.telephony.CarrierConfigManager; -import android.telephony.Rlog; -import android.telephony.SubscriptionManager; -import android.text.TextUtils; -import android.net.ICaptivePortal; -import android.view.ContextThemeWrapper; -import android.view.WindowManager; -import com.android.carrierdefaultapp.R; -import com.android.internal.telephony.PhoneConstants; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.util.ArrayUtils; - -import static android.net.CaptivePortal.APP_RETURN_DISMISSED; - -/** - * Activity that launches in response to the captive portal notification - * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION - * This activity requests network connection if there is no available one, launches the - * {@link com.android.captiveportallogin portalApp} and keeps track of the portal activation result. - */ -public class CaptivePortalLaunchActivity extends Activity { - private static final String TAG = CaptivePortalLaunchActivity.class.getSimpleName(); - private static final boolean DBG = true; - public static final int NETWORK_REQUEST_TIMEOUT_IN_MS = 5 * 1000; - - private ConnectivityManager mCm = null; - private ConnectivityManager.NetworkCallback mCb = null; - /* Progress dialogue when request network connection for captive portal */ - private AlertDialog mProgressDialog = null; - /* Alert dialogue when network request is timeout */ - private AlertDialog mAlertDialog = null; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mCm = ConnectivityManager.from(this); - // Check network connection before loading portal - Network network = getNetworkForCaptivePortal(); - NetworkInfo nwInfo = mCm.getNetworkInfo(network); - if (nwInfo == null || !nwInfo.isConnected()) { - if (DBG) logd("Network unavailable, request restricted connection"); - requestNetwork(getIntent()); - } else { - launchCaptivePortal(getIntent(), network); - } - } - - // show progress dialog during network connecting - private void showConnectingProgressDialog() { - mProgressDialog = new ProgressDialog(getApplicationContext()); - mProgressDialog.setMessage(getString(R.string.progress_dialogue_network_connection)); - mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); - mProgressDialog.show(); - } - - // if network request is timeout, show alert dialog with two option: cancel & wait - private void showConnectionTimeoutAlertDialog() { - mAlertDialog = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialog)) - .setMessage(getString(R.string.alert_dialogue_network_timeout)) - .setTitle(getString(R.string.alert_dialogue_network_timeout_title)) - .setNegativeButton(getString(R.string.quit), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // cancel - dismissDialog(mAlertDialog); - finish(); - } - }) - .setPositiveButton(getString(R.string.wait), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // wait, request network again - dismissDialog(mAlertDialog); - requestNetwork(getIntent()); - } - }) - .create(); - mAlertDialog.show(); - } - - private void requestNetwork(final Intent intent) { - NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .build(); - - mCb = new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - if (DBG) logd("Network available: " + network); - dismissDialog(mProgressDialog); - mCm.bindProcessToNetwork(network); - launchCaptivePortal(intent, network); - } - - @Override - public void onUnavailable() { - if (DBG) logd("Network unavailable"); - dismissDialog(mProgressDialog); - showConnectionTimeoutAlertDialog(); - } - }; - showConnectingProgressDialog(); - mCm.requestNetwork(request, mCb, NETWORK_REQUEST_TIMEOUT_IN_MS); - } - - private void releaseNetworkRequest() { - logd("release Network Request"); - if (mCb != null) { - mCm.unregisterNetworkCallback(mCb); - mCb = null; - } - } - - private void dismissDialog(AlertDialog dialog) { - if (dialog != null) { - dialog.dismiss(); - } - } - - private Network getNetworkForCaptivePortal() { - Network[] info = mCm.getAllNetworks(); - if (!ArrayUtils.isEmpty(info)) { - for (Network nw : info) { - final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw); - if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) - && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - return nw; - } - } - } - return null; - } - - private void launchCaptivePortal(final Intent intent, Network network) { - String redirectUrl = intent.getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY); - int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, - SubscriptionManager.getDefaultVoiceSubscriptionId()); - if (TextUtils.isEmpty(redirectUrl) || !matchUrl(redirectUrl, subId)) { - loge("Launch portal fails due to incorrect redirection URL: " + - Rlog.pii(TAG, redirectUrl)); - return; - } - final Intent portalIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); - portalIntent.putExtra(ConnectivityManager.EXTRA_NETWORK, network); - portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, - new CaptivePortal(new ICaptivePortal.Stub() { - @Override - public void appResponse(int response) { - logd("portal response code: " + response); - releaseNetworkRequest(); - if (response == APP_RETURN_DISMISSED) { - // Upon success http response code, trigger re-evaluation - CarrierActionUtils.applyCarrierAction( - CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, intent, - getApplicationContext()); - CarrierActionUtils.applyCarrierAction( - CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, intent, - getApplicationContext()); - CarrierActionUtils.applyCarrierAction( - CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, - intent, getApplicationContext()); - } - } - })); - portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, redirectUrl); - portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK); - if (DBG) logd("launching portal"); - startActivity(portalIntent); - finish(); - } - - // match configured redirection url - private boolean matchUrl(String url, int subId) { - CarrierConfigManager configManager = getApplicationContext() - .getSystemService(CarrierConfigManager.class); - String[] redirectURLs = configManager.getConfigForSubId(subId).getStringArray( - CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY); - if (ArrayUtils.isEmpty(redirectURLs)) { - if (DBG) logd("match is unnecessary without any configured redirection url"); - return true; - } - for (String redirectURL : redirectURLs) { - if (url.startsWith(redirectURL)) { - return true; - } - } - if (DBG) loge("no match found for configured redirection url"); - return false; - } - - private static void logd(String s) { - Rlog.d(TAG, s); - } - - private static void loge(String s) { - Rlog.d(TAG, s); - } -} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java new file mode 100644 index 000000000000..a5820f2da56c --- /dev/null +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -0,0 +1,434 @@ +/* + * 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.carrierdefaultapp; + +import android.app.Activity; +import android.app.LoadedApk; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.Proxy; +import android.net.TrafficStats; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Bundle; +import android.telephony.CarrierConfigManager; +import android.telephony.Rlog; +import android.telephony.SubscriptionManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.TypedValue; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.ArrayUtils; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Random; + +/** + * Activity that launches in response to the captive portal notification + * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION + * This activity requests network connection if there is no available one before loading the real + * portal page and apply carrier actions on the portal activation result. + */ +public class CaptivePortalLoginActivity extends Activity { + private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName(); + private static final boolean DBG = true; + + private static final int SOCKET_TIMEOUT_MS = 10 * 1000; + public static final int NETWORK_REQUEST_TIMEOUT_MS = 5 * 1000; + + private URL mUrl; + private Network mNetwork; + private NetworkCallback mNetworkCallback; + private ConnectivityManager mCm; + private WebView mWebView; + private MyWebViewClient mWebViewClient; + private boolean mLaunchBrowser = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mCm = ConnectivityManager.from(this); + mUrl = getUrlForCaptivePortal(); + if (mUrl == null) { + done(false); + return; + } + if (DBG) logd(String.format("onCreate for %s", mUrl.toString())); + setContentView(R.layout.activity_captive_portal_login); + getActionBar().setDisplayShowHomeEnabled(false); + + mWebView = (WebView) findViewById(R.id.webview); + mWebView.clearCache(true); + WebSettings webSettings = mWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); + mWebViewClient = new MyWebViewClient(); + mWebView.setWebViewClient(mWebViewClient); + mWebView.setWebChromeClient(new MyWebChromeClient()); + + mNetwork = getNetworkForCaptivePortal(); + if (mNetwork == null) { + requestNetworkForCaptivePortal(); + } else { + mCm.bindProcessToNetwork(mNetwork); + // Start initial page load so WebView finishes loading proxy settings. + // Actual load of mUrl is initiated by MyWebViewClient. + mWebView.loadData("", "text/html", null); + } + } + + @Override + public void onBackPressed() { + WebView myWebView = (WebView) findViewById(R.id.webview); + if (myWebView.canGoBack() && mWebViewClient.allowBack()) { + myWebView.goBack(); + } else { + super.onBackPressed(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + releaseNetworkRequest(); + if (mLaunchBrowser) { + // Give time for this network to become default. After 500ms just proceed. + for (int i = 0; i < 5; i++) { + // TODO: This misses when mNetwork underlies a VPN. + if (mNetwork.equals(mCm.getActiveNetwork())) break; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + final String url = mUrl.toString(); + if (DBG) logd("starting activity with intent ACTION_VIEW for " + url); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + } + } + + // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties. + private void setWebViewProxy() { + LoadedApk loadedApk = getApplication().mLoadedApk; + try { + Field receiversField = LoadedApk.class.getDeclaredField("mReceivers"); + receiversField.setAccessible(true); + ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); + for (Object receiverMap : receivers.values()) { + for (Object rec : ((ArrayMap) receiverMap).keySet()) { + Class clazz = rec.getClass(); + if (clazz.getName().contains("ProxyChangeListener")) { + Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, + Intent.class); + Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); + onReceiveMethod.invoke(rec, getApplicationContext(), intent); + Log.v(TAG, "Prompting WebView proxy reload."); + } + } + } + } catch (Exception e) { + loge("Exception while setting WebView proxy: " + e); + } + } + + private void done(boolean success) { + if (DBG) logd(String.format("Result success %b for %s", success, mUrl.toString())); + if (success) { + // Trigger re-evaluation upon success http response code + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, getIntent(), + getApplicationContext()); + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, getIntent(), + getApplicationContext()); + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(), + getApplicationContext()); + + } + finishAndRemoveTask(); + } + + private URL getUrlForCaptivePortal() { + String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY); + if (url.isEmpty()) { + url = mCm.getCaptivePortalServerUrl(); + } + final CarrierConfigManager configManager = getApplicationContext() + .getSystemService(CarrierConfigManager.class); + final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + final String[] portalURLs = configManager.getConfigForSubId(subId).getStringArray( + CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY); + if (!ArrayUtils.isEmpty(portalURLs)) { + for (String portalUrl : portalURLs) { + if (url.startsWith(portalUrl)) { + break; + } + } + url = null; + } + try { + return new URL(url); + } catch (MalformedURLException e) { + loge("Invalid captive portal URL " + url); + } + return null; + } + + private void testForCaptivePortal() { + new Thread(new Runnable() { + public void run() { + // Give time for captive portal to open. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + HttpURLConnection urlConnection = null; + int httpResponseCode = 500; + int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + try { + urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl); + urlConnection.setInstanceFollowRedirects(false); + urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); + urlConnection.setUseCaches(false); + urlConnection.getInputStream(); + httpResponseCode = urlConnection.getResponseCode(); + } catch (IOException e) { + } finally { + if (urlConnection != null) urlConnection.disconnect(); + TrafficStats.setThreadStatsTag(oldTag); + } + if (httpResponseCode == 204) { + done(true); + } + } + }).start(); + } + + private Network getNetworkForCaptivePortal() { + Network[] info = mCm.getAllNetworks(); + if (!ArrayUtils.isEmpty(info)) { + for (Network nw : info) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw); + if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + return nw; + } + } + } + return null; + } + + private void requestNetworkForCaptivePortal() { + NetworkRequest request = new NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .build(); + + mNetworkCallback = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + if (DBG) logd("Network available: " + network); + mCm.bindProcessToNetwork(network); + mNetwork = network; + runOnUiThreadIfNotFinishing(() -> { + // Start initial page load so WebView finishes loading proxy settings. + // Actual load of mUrl is initiated by MyWebViewClient. + mWebView.loadData("", "text/html", null); + }); + } + + @Override + public void onUnavailable() { + if (DBG) logd("Network unavailable"); + runOnUiThreadIfNotFinishing(() -> { + // Instead of not loading anything in webview, simply load the page and return + // HTTP error page in the absence of network connection. + mWebView.loadUrl(mUrl.toString()); + }); + } + }; + logd("request Network for captive portal"); + mCm.requestNetwork(request, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MS); + } + + private void releaseNetworkRequest() { + logd("release Network for captive portal"); + if (mNetworkCallback != null) { + mCm.unregisterNetworkCallback(mNetworkCallback); + mNetworkCallback = null; + mNetwork = null; + } + } + + private class MyWebViewClient extends WebViewClient { + private static final String INTERNAL_ASSETS = "file:///android_asset/"; + private final String mBrowserBailOutToken = Long.toString(new Random().nextLong()); + // How many Android device-independent-pixels per scaled-pixel + // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp) + private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1, + getResources().getDisplayMetrics()) + / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, + getResources().getDisplayMetrics()); + private int mPagesLoaded; + + // If we haven't finished cleaning up the history, don't allow going back. + public boolean allowBack() { + return mPagesLoaded > 1; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + if (url.contains(mBrowserBailOutToken)) { + mLaunchBrowser = true; + done(false); + return; + } + // The first page load is used only to cause the WebView to + // fetch the proxy settings. Don't update the URL bar, and + // don't check if the captive portal is still there. + if (mPagesLoaded == 0) return; + // For internally generated pages, leave URL bar listing prior URL as this is the URL + // the page refers to. + if (!url.startsWith(INTERNAL_ASSETS)) { + final TextView myUrlBar = (TextView) findViewById(R.id.url_bar); + myUrlBar.setText(url); + } + if (mNetwork != null) { + testForCaptivePortal(); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + mPagesLoaded++; + if (mPagesLoaded == 1) { + // Now that WebView has loaded at least one page we know it has read in the proxy + // settings. Now prompt the WebView read the Network-specific proxy settings. + setWebViewProxy(); + // Load the real page. + view.loadUrl(mUrl.toString()); + return; + } else if (mPagesLoaded == 2) { + // Prevent going back to empty first page. + view.clearHistory(); + } + if (mNetwork != null) { + testForCaptivePortal(); + } + } + + // Convert Android device-independent-pixels (dp) to HTML size. + private String dp(int dp) { + // HTML px's are scaled just like dp's, so just add "px" suffix. + return Integer.toString(dp) + "px"; + } + + // Convert Android scaled-pixels (sp) to HTML size. + private String sp(int sp) { + // Convert sp to dp's. + float dp = sp * mDpPerSp; + // Apply a scale factor to make things look right. + dp *= 1.3; + // Convert dp's to HTML size. + return dp((int) dp); + } + + // A web page consisting of a large broken lock icon to indicate SSL failure. + private final String SSL_ERROR_HTML = "<html><head><style>" + + "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; " + + "margin-top:" + dp(96) + "; background-color:#fafafa; }" + + "img { width:" + dp(48) + "; height:" + dp(48) + "; }" + + "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; " + + " opacity:0.87; line-height:1.28; }" + + "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; " + + " opacity:0.54; line-height:1.21905; }" + + "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; " + + " margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; " + + " height:" + dp(48) + "; font-weight:bold; }" + + "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>" + + "<div class=warn>%s</div>" + + "<div class=example>%s</div>" + "<a href=%s>%s</a></body></html>"; + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " + // Only show host to avoid leaking private info. + + Uri.parse(error.getUrl()).getHost() + " certificate: " + + error.getCertificate() + "); displaying SSL warning."); + final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning), + getString(R.string.ssl_error_example), mBrowserBailOutToken, + getString(R.string.ssl_error_continue)); + view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url.startsWith("tel:")) { + startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url))); + return true; + } + return false; + } + } + + private class MyWebChromeClient extends WebChromeClient { + @Override + public void onProgressChanged(WebView view, int newProgress) { + final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar); + myProgressBar.setProgress(newProgress); + } + } + + private void runOnUiThreadIfNotFinishing(Runnable r) { + if (!isFinishing()) { + runOnUiThread(r); + } + } + + private static void logd(String s) { + Rlog.d(TAG, s); + } + + private static void loge(String s) { + Rlog.d(TAG, s); + } + +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java index d9bd2fcc0acb..73ff3a9b5d1e 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java @@ -112,8 +112,10 @@ public class CarrierActionUtils { logd("onShowCaptivePortalNotification"); final NotificationManager notificationMgr = context.getSystemService( NotificationManager.class); - Intent portalIntent = new Intent(context, CaptivePortalLaunchActivity.class); + Intent portalIntent = new Intent(context, CaptivePortalLoginActivity.class); portalIntent.putExtras(intent); + portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT + | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, portalIntent, PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = getNotification(context, R.string.portal_notification_id, diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java deleted file mode 100644 index 8a18d7229435..000000000000 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.android.carrierdefaultapp; - -import android.annotation.TargetApi; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkRequest; - -import com.android.internal.telephony.TelephonyIntents; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class LaunchCaptivePortalActivityTest extends - CarrierDefaultActivityTestCase<CaptivePortalLaunchActivity> { - - @Mock - private ConnectivityManager mCm; - @Mock - private NetworkInfo mNetworkInfo; - @Mock - private Network mNetwork; - - @Captor - private ArgumentCaptor<Integer> mInt; - @Captor - private ArgumentCaptor<NetworkRequest> mNetworkReq; - - private NetworkCapabilities mNetworkCapabilities; - - public LaunchCaptivePortalActivityTest() { - super(CaptivePortalLaunchActivity.class); - } - - @Before - public void setUp() throws Exception { - super.setUp(); - injectSystemService(ConnectivityManager.class, mCm); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); - } - - @Override - protected Intent createActivityIntent() { - Intent intent = new Intent(getInstrumentation().getTargetContext(), - CaptivePortalLaunchActivity.class); - intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, "url"); - return intent; - } - - @Test - public void testWithoutInternetConnection() throws Throwable { - startActivity(); - TestContext.waitForMs(100); - verify(mCm, atLeast(1)).requestNetwork(mNetworkReq.capture(), any(), mInt.capture()); - // verify network request - assert(mNetworkReq.getValue().networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_INTERNET)); - assert(mNetworkReq.getValue().networkCapabilities.hasTransport( - NetworkCapabilities.TRANSPORT_CELLULAR)); - assertFalse(mNetworkReq.getValue().networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); - assertEquals(CaptivePortalLaunchActivity.NETWORK_REQUEST_TIMEOUT_IN_MS, - (int) mInt.getValue()); - // verify captive portal app is not launched due to unavailable network - assertNull(getStartedActivityIntent()); - stopActivity(); - } - - @Test - public void testWithInternetConnection() throws Throwable { - // Mock internet connection - mNetworkCapabilities = new NetworkCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - doReturn(new Network[]{mNetwork}).when(mCm).getAllNetworks(); - doReturn(mNetworkCapabilities).when(mCm).getNetworkCapabilities(eq(mNetwork)); - doReturn(mNetworkInfo).when(mCm).getNetworkInfo(eq(mNetwork)); - doReturn(true).when(mNetworkInfo).isConnected(); - - startActivity(); - TestContext.waitForMs(100); - // verify there is no network request with internet connection - verify(mCm, times(0)).requestNetwork(any(), any(), anyInt()); - // verify captive portal app is launched - assertNotNull(getStartedActivityIntent()); - assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN, - getStartedActivityIntent().getAction()); - stopActivity(); - } -} diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml index 5e49d058cd84..c6837fa30925 100644 --- a/packages/SystemUI/res/layout/pip_menu_activity.xml +++ b/packages/SystemUI/res/layout/pip_menu_activity.xml @@ -15,50 +15,55 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/menu" + android:id="@+id/background" android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="#4D000000"> - <!-- The above background is only for the dismiss button ripple to show. --> + android:layout_height="match_parent"> - <ImageView - android:id="@+id/dismiss" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="top|end" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_close" - android:src="@drawable/ic_close_white" - android:background="?android:selectableItemBackgroundBorderless" /> + <!-- Menu layout --> + <FrameLayout + android:id="@+id/menu_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> - <!-- The margins for this container is calculated in the code depending on whether the - actions_container is visible. --> - <FrameLayout - android:id="@+id/expand_container" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/expand_button" - android:layout_width="60dp" - android:layout_height="60dp" - android:layout_gravity="center" - android:contentDescription="@string/pip_phone_expand" - android:background="?android:selectableItemBackgroundBorderless" /> - </FrameLayout> + <ImageView + android:id="@+id/dismiss" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:layout_gravity="top|end" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_close" + android:src="@drawable/ic_close_white" + android:background="?android:selectableItemBackgroundBorderless" /> - <FrameLayout - android:id="@+id/actions_container" - android:layout_width="match_parent" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="bottom" - android:visibility="invisible"> - <LinearLayout - android:id="@+id/actions_group" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal" - android:orientation="horizontal" - android:divider="@android:color/transparent" - android:showDividers="middle" /> - </FrameLayout> + <!-- The margins for this container is calculated in the code depending on whether the + actions_container is visible. --> + <FrameLayout + android:id="@+id/expand_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/expand_button" + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_gravity="center" + android:contentDescription="@string/pip_phone_expand" + android:background="?android:selectableItemBackgroundBorderless" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/actions_container" + android:layout_width="match_parent" + android:layout_height="@dimen/pip_action_size" + android:layout_gravity="bottom" + android:visibility="invisible"> + <LinearLayout + android:id="@+id/actions_group" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:orientation="horizontal" + android:divider="@android:color/transparent" + android:showDividers="middle" /> + </FrameLayout> + </FrameLayout> </FrameLayout> diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 86e2c4956070..2f9c3fc3368a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -20,10 +20,12 @@ import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACT import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS; import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS; +import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -35,6 +37,8 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Handler; @@ -71,13 +75,19 @@ public class PipMenuActivity extends Activity { public static final int MESSAGE_POKE_MENU = 2; public static final int MESSAGE_HIDE_MENU = 3; public static final int MESSAGE_UPDATE_ACTIONS = 4; + public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; private static final long INITIAL_DISMISS_DELAY = 2000; private static final long POST_INTERACTION_DISMISS_DELAY = 1500; private static final long MENU_FADE_DURATION = 125; + private static final float MENU_BACKGROUND_ALPHA = 0.3f; + private static final float DISMISS_BACKGROUND_ALPHA = 0.8f; + private boolean mMenuVisible; private final List<RemoteAction> mActions = new ArrayList<>(); + private View mViewRoot; + private Drawable mBackgroundDrawable; private View mMenuContainer; private LinearLayout mActionsGroup; private View mDismissButton; @@ -85,6 +95,14 @@ public class PipMenuActivity extends Activity { private int mBetweenActionPaddingLand; private ObjectAnimator mMenuContainerAnimator; + private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final float alpha = (float) animation.getAnimatedValue(); + mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255)); + } + }; private PointF mDownPosition = new PointF(); private PointF mDownDelta = new PointF(); @@ -109,6 +127,10 @@ public class PipMenuActivity extends Activity { Pair<Rect, ParceledListSlice> data = (Pair<Rect, ParceledListSlice>) msg.obj; setActions(data.first, data.second.getList()); break; + case MESSAGE_UPDATE_DISMISS_FRACTION: + float fraction = (float) msg.obj; + updateDismissFraction(fraction); + break; } } }); @@ -130,7 +152,12 @@ public class PipMenuActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.pip_menu_activity); - mMenuContainer = findViewById(R.id.menu); + mBackgroundDrawable = new ColorDrawable(Color.BLACK); + mBackgroundDrawable.setAlpha(0); + mViewRoot = findViewById(R.id.background); + mViewRoot.setBackground(mBackgroundDrawable); + mMenuContainer = findViewById(R.id.menu_container); + mMenuContainer.setAlpha(0); mMenuContainer.setOnClickListener((v) -> { expandPip(); }); @@ -222,10 +249,10 @@ public class PipMenuActivity extends Activity { private void showMenu(Rect stackBounds, Rect movementBounds) { if (!mMenuVisible) { + updateActionViews(stackBounds); if (mMenuContainerAnimator != null) { mMenuContainerAnimator.cancel(); } - notifyMenuVisibility(true); updateExpandButtonFromBounds(stackBounds, movementBounds); mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, @@ -238,6 +265,7 @@ public class PipMenuActivity extends Activity { repostDelayedFinish(INITIAL_DISMISS_DELAY); } }); + mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener); mMenuContainerAnimator.start(); } else { repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); @@ -269,20 +297,24 @@ public class PipMenuActivity extends Activity { } } }); + mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener); mMenuContainerAnimator.start(); } } private void updateFromIntent(Intent intent) { - Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS)); - Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra( - EXTRA_MOVEMENT_BOUNDS)); mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER); ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS); if (actions != null) { - setActions(stackBounds, actions.getList()); + mActions.clear(); + mActions.addAll(actions.getList()); + } + if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) { + Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS)); + Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra( + EXTRA_MOVEMENT_BOUNDS)); + showMenu(stackBounds, movementBounds); } - showMenu(stackBounds, movementBounds); } private void updateExpandButtonFromBounds(Rect stackBounds, Rect movementBounds) { @@ -365,6 +397,19 @@ public class PipMenuActivity extends Activity { } } + private void updateDismissFraction(float fraction) { + int alpha; + if (mMenuVisible) { + mMenuContainer.setAlpha(1-fraction); + final float interpolatedAlpha = + MENU_BACKGROUND_ALPHA * (1.0f - fraction) + DISMISS_BACKGROUND_ALPHA * fraction; + alpha = (int) (interpolatedAlpha*255); + } else { + alpha = (int) (fraction*DISMISS_BACKGROUND_ALPHA*255); + } + mBackgroundDrawable.setAlpha(alpha); + } + private void notifyRegisterInputConsumer() { Message m = Message.obtain(); m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index badf64b90b8f..7dc455bd0d2c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -42,7 +42,7 @@ import java.util.ArrayList; import java.util.List; /** - * Manages the PiP menu activity. + * Manages the PiP menu activity which can show menu options or a scrim. * * The current media session provides actions whenever there are no valid actions provided by the * current PiP activity. Otherwise, those actions always take precedence. @@ -55,6 +55,7 @@ public class PipMenuActivityController { public static final String EXTRA_ACTIONS = "actions"; public static final String EXTRA_STACK_BOUNDS = "stack_bounds"; public static final String EXTRA_MOVEMENT_BOUNDS = "movement_bounds"; + public static final String EXTRA_SHOW_MENU = "show_menu"; public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100; public static final int MESSAGE_EXPAND_PIP = 101; @@ -101,6 +102,7 @@ public class PipMenuActivityController { private ParceledListSlice mMediaActions; private boolean mMenuVisible; + private boolean mStartActivityRequested; private Messenger mToActivityMessenger; private Messenger mMessenger = new Messenger(new Handler() { @Override @@ -135,6 +137,7 @@ public class PipMenuActivityController { } case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { mToActivityMessenger = msg.replyTo; + mStartActivityRequested = false; // Mark the menu as invisible once the activity finishes as well if (mToActivityMessenger == null) { onMenuVisibilityChanged(false, true /* resize */); @@ -179,6 +182,25 @@ public class PipMenuActivityController { } /** + * Updates the appearance of the menu and scrim on top of the PiP while dismissing. + */ + public void setDismissFraction(float fraction) { + if (mToActivityMessenger != null) { + Message m = Message.obtain(); + m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION; + m.obj = fraction; + try { + mToActivityMessenger.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Could not notify menu to show", e); + } + } else if (!mStartActivityRequested) { + startMenuActivity(null /* stackBounds */, null /* movementBounds */, + false /* showMenu */); + } + } + + /** * Shows the menu activity. */ public void showMenu(Rect stackBounds, Rect movementBounds) { @@ -191,28 +213,8 @@ public class PipMenuActivityController { } catch (RemoteException e) { Log.e(TAG, "Could not notify menu to show", e); } - } else { - // Start the menu activity on the top task of the pinned stack - try { - StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); - if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && - pinnedStackInfo.taskIds.length > 0) { - Intent intent = new Intent(mContext, PipMenuActivity.class); - intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); - intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); - intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString()); - intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString()); - ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); - options.setLaunchTaskId( - pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]); - options.setTaskOverlay(true, true /* canResume */); - mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); - } else { - Log.e(TAG, "No PIP tasks found"); - } - } catch (RemoteException e) { - Log.e(TAG, "Error showing PIP menu activity", e); - } + } else if (!mStartActivityRequested) { + startMenuActivity(stackBounds, movementBounds, true /* showMenu */); } } @@ -272,6 +274,39 @@ public class PipMenuActivityController { } /** + * Starts the menu activity on the top task of the pinned stack. + */ + private void startMenuActivity(Rect stackBounds, Rect movementBounds, boolean showMenu) { + try { + StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && + pinnedStackInfo.taskIds.length > 0) { + Intent intent = new Intent(mContext, PipMenuActivity.class); + intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); + intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); + if (stackBounds != null) { + intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString()); + } + if (movementBounds != null) { + intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString()); + } + intent.putExtra(EXTRA_SHOW_MENU, showMenu); + ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); + options.setLaunchTaskId( + pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]); + options.setTaskOverlay(true, true /* canResume */); + mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); + mStartActivityRequested = true; + } else { + Log.e(TAG, "No PIP tasks found"); + } + } catch (RemoteException e) { + mStartActivityRequested = false; + Log.e(TAG, "Error showing PIP menu activity", e); + } + } + + /** * Updates the PiP menu activity with the best set of actions provided. */ private void updateMenuActions() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 49d89a2bdd68..c4cf28c9a320 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -65,9 +65,9 @@ public class PipMotionHelper { private static final int IME_SHIFT_DURATION = 300; // The fraction of the stack width that the user has to drag offscreen to minimize the PiP - private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f; - // The fraction of the stack height that the user has to drag offscreen to minimize the PiP - private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f; + private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f; + // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP + private static final float DISMISS_OFFSCREEN_FRACTION = 0.15f; private Context mContext; private IActivityManager mActivityManager; @@ -234,12 +234,16 @@ public class PipMotionHelper { /** * Animates the PiP to the minimized state, slightly offscreen. */ - Rect animateToClosestMinimizedState(Rect movementBounds) { + Rect animateToClosestMinimizedState(Rect movementBounds, + AnimatorUpdateListener updateListener) { cancelAnimations(); Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds); if (!mBounds.equals(toBounds)) { mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener); + if (updateListener != null) { + mBoundsAnimator.addUpdateListener(updateListener); + } mBoundsAnimator.start(); } return toBounds; @@ -248,7 +252,8 @@ public class PipMotionHelper { /** * Flings the PiP to the closest snap target. */ - Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds) { + Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds, + AnimatorUpdateListener listener) { cancelAnimations(); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds, velocityX, velocityY); @@ -258,6 +263,9 @@ public class PipMotionHelper { mFlingAnimationUtils.apply(mBoundsAnimator, 0, distanceBetweenRectOffsets(mBounds, toBounds), velocity); + if (listener != null) { + mBoundsAnimator.addUpdateListener(listener); + } mBoundsAnimator.start(); } return toBounds; @@ -266,12 +274,15 @@ public class PipMotionHelper { /** * Animates the PiP to the closest snap target. */ - Rect animateToClosestSnapTarget(Rect movementBounds) { + Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) { cancelAnimations(); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds); if (!mBounds.equals(toBounds)) { mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener); + if (listener != null) { + mBoundsAnimator.addUpdateListener(listener); + } mBoundsAnimator.start(); } return toBounds; @@ -316,7 +327,7 @@ public class PipMotionHelper { /** * Animates the dismissal of the PiP off the edge of the screen. */ - Rect animateDragToEdgeDismiss(Rect pipBounds) { + Rect animateDragToEdgeDismiss(Rect pipBounds, AnimatorUpdateListener listener) { cancelAnimations(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); @@ -330,6 +341,9 @@ public class PipMotionHelper { dismissPip(); } }); + if (listener != null) { + mBoundsAnimator.addUpdateListener(listener); + } mBoundsAnimator.start(); return toBounds; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 80231a954e1a..c52fc3e87b0e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -16,6 +16,8 @@ package com.android.systemui.pip.phone; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.IActivityManager; import android.content.Context; import android.graphics.Point; @@ -55,7 +57,7 @@ public class PipTouchHandler { // Allow dragging the PIP to a location to close it private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false; - private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = false; + private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true; private final Context mContext; private final IActivityManager mActivityManager; @@ -87,6 +89,13 @@ public class PipTouchHandler { } } }; + private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener = + new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateDismissFraction(); + } + }; // Behaviour states private boolean mIsMenuVisible; @@ -124,7 +133,7 @@ public class PipTouchHandler { @Override public void onPipMinimize() { setMinimizedStateInternal(true); - mMotionHelper.animateToClosestMinimizedState(mMovementBounds); + mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */); } @Override @@ -331,6 +340,22 @@ public class PipTouchHandler { } /** + * Updates the appearance of the menu and scrim on top of the PiP while dismissing. + */ + void updateDismissFraction() { + if (mMenuController != null) { + Rect bounds = mMotionHelper.getBounds(); + final float target = mMovementBounds.bottom + bounds.height(); + float fraction = 0f; + if (bounds.bottom > target) { + final float distance = bounds.bottom - target; + fraction = Math.min(distance / bounds.height(), 1f); + } + mMenuController.setDismissFraction(fraction); + } + } + + /** * Sets the controller to update the system of changes from user interaction. */ void setPinnedStackController(IPinnedStackController controller) { @@ -465,6 +490,9 @@ public class PipTouchHandler { if (ENABLE_DISMISS_DRAG_TO_TARGET) { mDismissViewController.updateDismissTarget(mTmpBounds); } + if (ENABLE_DISMISS_DRAG_TO_EDGE) { + updateDismissFraction(); + } return true; } return false; @@ -502,7 +530,8 @@ public class PipTouchHandler { boolean isFlingToBot = isFlingTowardsEdge(touchState, 4 /* bottom */); if (ENABLE_DISMISS_DRAG_TO_EDGE && (mMotionHelper.shouldDismissPip() || isFlingToBot)) { - mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds()); + mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds(), + mUpdateScrimListener); MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, METRIC_VALUE_DISMISSED_BY_DRAG); @@ -517,7 +546,8 @@ public class PipTouchHandler { // minimize offset adjusted mMenuController.hideMenu(); } else { - mMotionHelper.animateToClosestMinimizedState(mMovementBounds); + mMotionHelper.animateToClosestMinimizedState(mMovementBounds, + mUpdateScrimListener); } return true; } @@ -536,13 +566,14 @@ public class PipTouchHandler { final PointF vel = mTouchState.getVelocity(); final float velocity = PointF.length(vel.x, vel.y); if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds); + mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds, + mUpdateScrimListener); } else { - mMotionHelper.animateToClosestSnapTarget(mMovementBounds); + mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener); } } else if (mIsMinimized) { // This was a tap, so no longer minimized - mMotionHelper.animateToClosestSnapTarget(mMovementBounds); + mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* listener */); setMinimizedStateInternal(false); } else if (!mIsMenuVisible) { mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 8a3c4e3b7d76..2b52b48a6819 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; +import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; @@ -46,6 +47,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.telecom.TelecomManager; import android.text.TextUtils; @@ -104,6 +106,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private int mNavigationIconHints = 0; private int mNavigationBarMode; private AccessibilityManager mAccessibilityManager; + private MagnificationContentObserver mMagnificationObserver; private int mDisabledFlags1; private StatusBar mStatusBar; @@ -135,6 +138,12 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); mAccessibilityManager.addAccessibilityServicesStateChangeListener( this::updateAccessibilityServicesState); + mMagnificationObserver = new MagnificationContentObserver( + getContext().getMainThreadHandler()); + getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, + mMagnificationObserver); + if (savedInstanceState != null) { mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); } @@ -154,6 +163,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mCommandQueue.removeCallbacks(this); mAccessibilityManager.removeAccessibilityServicesStateChangeListener( this::updateAccessibilityServicesState); + getContext().getContentResolver().unregisterContentObserver(mMagnificationObserver); try { WindowManagerGlobal.getWindowManagerService() .removeRotationWatcher(mRotationWatcher); @@ -387,6 +397,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); + updateAccessibilityServicesState(); } private boolean onHomeTouch(View v, MotionEvent event) { @@ -550,10 +561,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } private void updateAccessibilityServicesState() { + int requestingServices = 0; + try { + if (Settings.Secure.getInt(getContext().getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED) == 1) { + requestingServices++; + } + } catch (Settings.SettingNotFoundException e) { + } + final List<AccessibilityServiceInfo> services = mAccessibilityManager.getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_ALL_MASK); - int requestingServices = 0; for (int i = services.size() - 1; i >= 0; --i) { AccessibilityServiceInfo info = services.get(i); if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { @@ -600,6 +619,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mNavigationBarView.getBarTransitions().finishAnimations(); } + private class MagnificationContentObserver extends ContentObserver { + + public MagnificationContentObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + NavigationBarFragment.this.updateAccessibilityServicesState(); + } + } + private final Stub mRotationWatcher = new Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 03582ea48d2c..3800f297a8cc 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3604,6 +3604,10 @@ message MetricsEvent { // ACTION: Settings > Search > Click saved queries ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY = 881; + // OPEN: Settings > Security & screen lock -> Lock screen preferences + // CATEGORY: SETTINGS + SETTINGS_LOCK_SCREEN_PREFERENCES = 882; + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b4630efe80e8..c532efb79345 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -48,9 +48,6 @@ static constexpr bool kLogApi = false; using namespace android; -template <typename... T> -void UNUSED(T... t) {} - #define PER_ARRAY_TYPE(flag, fnc, readonly, ...) { \ jint len = 0; \ void *ptr = nullptr; \ diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index fd93865ea97e..9e4d89cbc9c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -77,9 +77,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010; - static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS - | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION - | FLAG_FEATURE_SCREEN_MAGNIFIER; /** * Flag for enabling the feature to control the screen magnifier. If * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored @@ -90,6 +87,16 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020; + /** + * Flag for enabling the feature to trigger the screen magnifier + * from another on-device interaction. + */ + static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040; + + static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS + | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION + | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; + private final Runnable mProcessBatchedEventsRunnable = new Runnable() { @Override public void run() { @@ -379,6 +386,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } + void notifyAccessibilityButtonClicked() { + if (mMagnificationGestureHandler != null) { + mMagnificationGestureHandler.notifyShortcutTriggered(); + } + } + private void enableFeatures() { resetStreamState(); @@ -393,11 +406,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 - || (mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) { + || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) + || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { final boolean detectControlGestures = (mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0; + final boolean triggerable = (mEnabledFeatures + & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0; mMagnificationGestureHandler = new MagnificationGestureHandler( - mContext, mAms, detectControlGestures); + mContext, mAms, detectControlGestures, triggerable); addFirstEventHandler(mMagnificationGestureHandler); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 98ce00e05914..397938ac3160 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -781,6 +781,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsTouchExplorationEnabled = false; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; + userState.mIsNavBarMagnificationEnabled = false; userState.mIsAutoclickEnabled = false; userState.mEnabledServices.clear(); } @@ -831,6 +832,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsTouchExplorationEnabled = touchExplorationEnabled; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; + userState.mIsNavBarMagnificationEnabled = false; userState.mIsAutoclickEnabled = false; userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); @@ -1152,11 +1154,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void notifyAccessibilityButtonClickedLocked() { final UserState state = getCurrentUserStateLocked(); - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final Service service = state.mBoundServices.get(i); - // TODO(b/34720082): Only notify a single user-defined service - if (service.mRequestAccessibilityButton) { - service.notifyAccessibilityButtonClickedLocked(); + if (state.mIsNavBarMagnificationEnabled) { + mMainHandler.obtainMessage( + MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget(); + } else { + for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { + final Service service = state.mBoundServices.get(i); + // TODO(b/34720082): Only notify a single user-defined service + if (service.mRequestAccessibilityButton) { + service.notifyAccessibilityButtonClickedLocked(); + } } } } @@ -1548,6 +1555,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (userState.mIsDisplayMagnificationEnabled) { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } + if (userState.mIsNavBarMagnificationEnabled) { + flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; + } if (userHasMagnificationServicesLocked(userState)) { flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER; } @@ -1781,7 +1791,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { somethingChanged |= readTouchExplorationEnabledSettingLocked(userState); somethingChanged |= readHighTextContrastEnabledSettingLocked(userState); somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState); - somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState); + somethingChanged |= readMagnificationEnabledSettingsLocked(userState); somethingChanged |= readAutoclickEnabledSettingLocked(userState); somethingChanged |= readAccessibilityShortcutSettingLocked(userState); return somethingChanged; @@ -1810,13 +1820,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } - private boolean readDisplayMagnificationEnabledSettingLocked(UserState userState) { + private boolean readMagnificationEnabledSettingsLocked(UserState userState) { final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userState.mUserId) == 1; - if (displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) { + final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + 0, userState.mUserId) == 1; + if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) + || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) { userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled; + userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled; return true; } return false; @@ -2018,8 +2034,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } - if (userState.mIsDisplayMagnificationEnabled || - userHasListeningMagnificationServicesLocked(userState)) { + if (userState.mIsDisplayMagnificationEnabled || userState.mIsNavBarMagnificationEnabled + || userHasListeningMagnificationServicesLocked(userState)) { // Initialize the magnification controller if necessary getMagnificationController(); mMagnificationController.register(); @@ -2241,6 +2257,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled); pw.append(", displayMagnificationEnabled=" + userState.mIsDisplayMagnificationEnabled); + pw.append(", navBarMagnificationEnabled=" + + userState.mIsNavBarMagnificationEnabled); pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled); if (userState.mUiAutomationService != null) { pw.append(", "); @@ -2320,6 +2338,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10; public static final int MSG_UPDATE_FINGERPRINT = 11; public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12; + public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13; public MainHandler(Looper looper) { super(looper); @@ -2406,6 +2425,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } }); } break; + + case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: { + synchronized (mLock) { + if (mHasInputFilter && mInputFilter != null) { + mInputFilter.notifyAccessibilityButtonClicked(); + } + } + } } } @@ -4788,6 +4815,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public boolean mIsTextHighContrastEnabled; public boolean mIsEnhancedWebAccessibilityEnabled; public boolean mIsDisplayMagnificationEnabled; + public boolean mIsNavBarMagnificationEnabled; public boolean mIsAutoclickEnabled; public boolean mIsPerformGesturesEnabled; public boolean mIsFilterKeyEventsEnabled; @@ -4856,6 +4884,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mIsTouchExplorationEnabled = false; mIsEnhancedWebAccessibilityEnabled = false; mIsDisplayMagnificationEnabled = false; + mIsNavBarMagnificationEnabled = false; mIsAutoclickEnabled = false; mSoftKeyboardShowMode = 0; @@ -4888,6 +4917,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); + private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED); @@ -4927,6 +4959,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri, + false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mAutoclickEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri, @@ -4966,8 +5000,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (readTouchExplorationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); } - } else if (mDisplayMagnificationEnabledUri.equals(uri)) { - if (readDisplayMagnificationEnabledSettingLocked(userState)) { + } else if (mDisplayMagnificationEnabledUri.equals(uri) + || mNavBarMagnificationEnabledUri.equals(uri)) { + if (readMagnificationEnabledSettingsLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mAutoclickEnabledUri.equals(uri)) { diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index f65046ce4350..caa74b9512d1 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -685,6 +685,12 @@ class MagnificationController implements Handler.Callback { } } + void setForceShowMagnifiableBounds(boolean show) { + if (mRegistered) { + mWindowManager.setForceShowMagnifiableBounds(show); + } + } + private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) { final float scale = getSentScale(); final float offsetX = getSentOffsetX(); diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index f6e5340ed7ee..7e82edaae3e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -16,7 +16,10 @@ package com.android.server.accessibility; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.util.MathUtils; @@ -57,22 +60,30 @@ import android.view.accessibility.AccessibilityEvent; * be the same but when the finger goes up the screen will stay magnified. * In other words, the initial magnified state is sticky. * - * 3. Pinching with any number of additional fingers when viewport dragging + * 3. Magnification can optionally be "triggered" by some external shortcut + * affordance. When this occurs via {@link #notifyShortcutTriggered()} a + * subsequent tap in a magnifiable region will engage permanent screen + * magnification as described in #1. Alternatively, a subsequent long-press + * or drag will engage magnification with viewport dragging as described in + * #2. Once magnified, all following behaviors apply whether magnification + * was engaged via a triple-tap or by a triggered shortcut. + * + * 4. Pinching with any number of additional fingers when viewport dragging * is enabled, i.e. the user triple tapped and holds, would adjust the * magnification scale which will become the current default magnification * scale. The next time the user magnifies the same magnification scale * would be used. * - * 4. When in a permanent magnified state the user can use two or more fingers + * 5. When in a permanent magnified state the user can use two or more fingers * to pan the viewport. Note that in this mode the content is panned as * opposed to the viewport dragging mode in which the viewport is moved. * - * 5. When in a permanent magnified state the user can use two or more + * 6. When in a permanent magnified state the user can use two or more * fingers to change the magnification scale which will become the current * default magnification scale. The next time the user magnifies the same * magnification scale would be used. * - * 6. The magnification scale will be persisted in settings and in the cloud. + * 7. The magnification scale will be persisted in settings and in the cloud. */ class MagnificationGestureHandler implements EventStreamTransformation { private static final String LOG_TAG = "MagnificationEventHandler"; @@ -94,8 +105,10 @@ class MagnificationGestureHandler implements EventStreamTransformation { private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler; private final StateViewportDraggingHandler mStateViewportDraggingHandler; + private final ScreenStateReceiver mScreenStateReceiver; - private final boolean mDetectControlGestures; + private final boolean mDetectTripleTap; + private final boolean mTriggerable; private EventStreamTransformation mNext; @@ -104,19 +117,39 @@ class MagnificationGestureHandler implements EventStreamTransformation { private boolean mTranslationEnabledBeforePan; + private boolean mShortcutTriggered; + private PointerCoords[] mTempPointerCoords; private PointerProperties[] mTempPointerProperties; private long mDelegatingStateDownTime; + /** + * @param context Context for resolving various magnification-related resources + * @param ams AccessibilityManagerService used to obtain a {@link MagnificationController} + * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap + * gestures for engaging and disengaging magnification, + * {@code false} if it should ignore such gestures + * @param triggerable {@code true} if this detector should be "triggerable" by some external + * shortcut invoking {@link #notifyShortcutTriggered}, {@code + * false} if it should ignore such triggers. + */ public MagnificationGestureHandler(Context context, AccessibilityManagerService ams, - boolean detectControlGestures) { + boolean detectTripleTap, boolean triggerable) { mMagnificationController = ams.getMagnificationController(); mDetectingStateHandler = new DetectingStateHandler(context); mStateViewportDraggingHandler = new StateViewportDraggingHandler(); mMagnifiedContentInteractionStateHandler = new MagnifiedContentInteractionStateHandler(context); - mDetectControlGestures = detectControlGestures; + mDetectTripleTap = detectTripleTap; + mTriggerable = triggerable; + + if (triggerable) { + mScreenStateReceiver = new ScreenStateReceiver(context, this); + mScreenStateReceiver.register(); + } else { + mScreenStateReceiver = null; + } transitionToState(STATE_DETECTING); } @@ -129,7 +162,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { } return; } - if (!mDetectControlGestures) { + if (!mDetectTripleTap && !mTriggerable) { if (mNext != null) { dispatchTransformedEvent(event, rawEvent, policyFlags); } @@ -151,7 +184,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { break; case STATE_MAGNIFIED_INTERACTION: { // mMagnifiedContentInteractionStateHandler handles events only - // if this is the current state since it uses ScaleGestureDetecotr + // if this is the current state since it uses ScaleGestureDetector // and a GestureDetector which need well formed event stream. } break; @@ -193,11 +226,34 @@ class MagnificationGestureHandler implements EventStreamTransformation { @Override public void onDestroy() { + if (mScreenStateReceiver != null) { + mScreenStateReceiver.unregister(); + } clear(); } + void notifyShortcutTriggered() { + if (mTriggerable) { + if (mMagnificationController.resetIfNeeded(true)) { + clear(); + } else { + setMagnificationShortcutTriggered(!mShortcutTriggered); + } + } + } + + private void setMagnificationShortcutTriggered(boolean state) { + if (mShortcutTriggered == state) { + return; + } + + mShortcutTriggered = state; + mMagnificationController.setForceShowMagnifiableBounds(state); + } + private void clear() { mCurrentState = STATE_DETECTING; + setMagnificationShortcutTriggered(false); mDetectingStateHandler.clear(); mStateViewportDraggingHandler.clear(); mMagnifiedContentInteractionStateHandler.clear(); @@ -575,31 +631,51 @@ class MagnificationGestureHandler implements EventStreamTransformation { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(!mShortcutTriggered); return; } - if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null - && GestureUtils.isMultiTap(mLastDownEvent, event, - mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { + if (mShortcutTriggered) { Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD, policyFlags, 0, event); mHandler.sendMessageDelayed(message, ViewConfiguration.getLongPressTimeout()); - } else if (mTapCount < ACTION_TAP_COUNT) { + return; + } + if (mDetectTripleTap) { + if ((mTapCount == ACTION_TAP_COUNT - 1) && (mLastDownEvent != null) + && GestureUtils.isMultiTap(mLastDownEvent, event, mMultiTapTimeSlop, + mMultiTapDistanceSlop, 0)) { + Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD, + policyFlags, 0, event); + mHandler.sendMessageDelayed(message, + ViewConfiguration.getLongPressTimeout()); + } else if (mTapCount < ACTION_TAP_COUNT) { + Message message = mHandler.obtainMessage( + MESSAGE_TRANSITION_TO_DELEGATING_STATE); + mHandler.sendMessageDelayed(message, mMultiTapTimeSlop); + } + clearLastDownEvent(); + mLastDownEvent = MotionEvent.obtain(event); + } else if (mMagnificationController.isMagnifying()) { + // If magnified, consume an ACTION_DOWN until mMultiTapTimeSlop or + // mTapDistanceSlop is reached to ensure MAGNIFIED_INTERACTION is reachable. Message message = mHandler.obtainMessage( MESSAGE_TRANSITION_TO_DELEGATING_STATE); mHandler.sendMessageDelayed(message, mMultiTapTimeSlop); + return; + } else { + transitionToDelegatingState(true); + return; } - clearLastDownEvent(); - mLastDownEvent = MotionEvent.obtain(event); } break; case MotionEvent.ACTION_POINTER_DOWN: { if (mMagnificationController.isMagnifying()) { + mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); transitionToState(STATE_MAGNIFIED_INTERACTION); clear(); } else { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); } } break; @@ -608,29 +684,34 @@ class MagnificationGestureHandler implements EventStreamTransformation { final double distance = GestureUtils.computeDistance(mLastDownEvent, event, 0); if (Math.abs(distance) > mTapDistanceSlop) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); } } } break; case MotionEvent.ACTION_UP: { - if (mLastDownEvent == null) { - return; - } - mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(!mShortcutTriggered); return; } + if (mShortcutTriggered) { + clear(); + onActionTap(event, policyFlags); + return; + } + if (mLastDownEvent == null) { + return; + } + mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop, mTapDistanceSlop, 0)) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); return; } - if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent, - event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { - transitionToDelegatingStateAndClear(); + if (mLastTapUpEvent != null && !GestureUtils.isMultiTap( + mLastTapUpEvent, event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { + transitionToDelegatingState(true); return; } mTapCount++; @@ -655,6 +736,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { @Override public void clear() { + setMagnificationShortcutTriggered(false); mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); clearTapDetectionState(); @@ -714,10 +796,12 @@ class MagnificationGestureHandler implements EventStreamTransformation { } } - private void transitionToDelegatingStateAndClear() { + private void transitionToDelegatingState(boolean andClear) { transitionToState(STATE_DELEGATING); sendDelayedMotionEvents(); - clear(); + if (andClear) { + clear(); + } } private void onActionTap(MotionEvent up, int policyFlags) { @@ -820,4 +904,30 @@ class MagnificationGestureHandler implements EventStreamTransformation { mPolicyFlags = 0; } } + + /** + * BroadcastReceiver used to cancel the magnification shortcut when the screen turns off + */ + private static class ScreenStateReceiver extends BroadcastReceiver { + private final Context mContext; + private final MagnificationGestureHandler mGestureHandler; + + public ScreenStateReceiver(Context context, MagnificationGestureHandler gestureHandler) { + mContext = context; + mGestureHandler = gestureHandler; + } + + public void register() { + mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } + + public void unregister() { + mContext.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) { + mGestureHandler.setMagnificationShortcutTriggered(false); + } + } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 75916be3759c..2885e663af3f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1584,9 +1584,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If the assistant stack is focused and translucent, then the docked stack is always // visible - if (topStack.isAssistantStack() - && topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) { - return STACK_VISIBLE; + if (topStack.isAssistantStack()) { + return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE + : STACK_INVISIBLE; } // Otherwise, the docked stack is always visible, except in the case where the top @@ -3216,7 +3216,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // Move the home stack to the top if this stack is fullscreen or there is no // other visible stack. - if (mStackSupervisor.moveHomeStackTaskToTop(myReason)) { + if (task.isOverHomeStack() && + mStackSupervisor.moveHomeStackTaskToTop(myReason)) { // Activity focus was already adjusted. Nothing else to do... return; } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 71bfa648bd5f..e1426fdcf3ef 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -245,9 +245,12 @@ public class LauncherAppsService extends SystemService { // STOPSHIP Remove the whitelist. if ("com.google.android.talk".equals(callingPackage) - || "com.google.android.quicksearchbox".equals(callingPackage)) { + || "com.google.android.quicksearchbox".equals(callingPackage) + || "com.google.android.googlequicksearchbox".equals(callingPackage) + ) { return false; } + // STOPSHIP Change it to 'e'. Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile " + targetUserId + " from " + callingUserId); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 12890190aefc..4ac1cce91402 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -10565,9 +10565,9 @@ public class PackageManagerService extends IPackageManager.Stub { ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi + " (requirer=" - + (requirer == null ? "null" : requirer.pkg.packageName) + + (requirer != null ? requirer.pkg : "null") + ", scannedPackage=" - + (scannedPackage != null ? scannedPackage.packageName : "null") + + (scannedPackage != null ? scannedPackage : "null") + ")"); try { mInstaller.rmdex(ps.codePathString, diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 1510dd126c74..5abc4e4d2e56 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -222,6 +222,14 @@ final class AccessibilityController { || mWindowsForAccessibilityObserver != null); } + /** NOTE: This has to be called within a surface transaction. */ + public void setForceShowMagnifiableBoundsLocked(boolean show) { + if (mDisplayMagnifier != null) { + mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show); + mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); + } + } + private static void populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix) { sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; @@ -266,6 +274,8 @@ final class AccessibilityController { private final long mLongAnimationDuration; + private boolean mForceShowMagnifiableBounds = false; + public DisplayMagnifier(WindowManagerService windowManagerService, MagnificationCallbacks callbacks) { mContext = windowManagerService.mContext; @@ -283,6 +293,15 @@ final class AccessibilityController { mWindowManagerService.scheduleAnimationLocked(); } + public void setForceShowMagnifiableBoundsLocked(boolean show) { + mForceShowMagnifiableBounds = show; + mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); + } + + public boolean isForceShowingMagnifiableBoundsLocked() { + return mForceShowMagnifiableBounds; + } + public void onRectangleOnScreenRequestedLocked(Rect rectangle) { if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); @@ -488,7 +507,8 @@ final class AccessibilityController { // to show the border. We will do so when the pending message is handled. if (!mHandler.hasMessages( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true); + setMagnifiedRegionBorderShownLocked( + isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); } } @@ -600,11 +620,11 @@ final class AccessibilityController { } public void onRotationChangedLocked() { - // If we are magnifying, hide the magnified border window immediately so + // If we are showing the magnification border, hide it immediately so // the user does not see strange artifacts during rotation. The screenshot - // used for rotation has already the border. After the rotation is complete + // used for rotation already has the border. After the rotation is complete // we will show the border. - if (isMagnifyingLocked()) { + if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { setMagnifiedRegionBorderShownLocked(false, false); final long delay = (long) (mLongAnimationDuration * mWindowManagerService.getWindowAnimationScaleLocked()); @@ -926,7 +946,8 @@ final class AccessibilityController { case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { synchronized (mWindowManagerService.mWindowMap) { - if (mMagnifedViewport.isMagnifyingLocked()) { + if (mMagnifedViewport.isMagnifyingLocked() + || isForceShowingMagnifiableBoundsLocked()) { mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); mWindowManagerService.scheduleAnimationLocked(); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 75a7385c9bf4..7a36da202aad 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -44,6 +44,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; @@ -1652,10 +1653,14 @@ public class AppTransition implements Dump { } int getAppStackClipMode() { + // When dismiss keyguard animation occurs, clip before the animation to prevent docked + // app from showing beyond the divider + if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY + || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { + return STACK_CLIP_BEFORE_ANIM; + } return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS - || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY - || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL ? STACK_CLIP_NONE : STACK_CLIP_AFTER_ANIM; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b063e01f3a2a..014a89dd25d7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7158,6 +7158,17 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setForceShowMagnifiableBounds(boolean show) { + synchronized (mWindowMap) { + if (mAccessibilityController != null) { + mAccessibilityController.setForceShowMagnifiableBoundsLocked(show); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + } + + @Override public void getMagnificationRegion(@NonNull Region magnificationRegion) { synchronized (mWindowMap) { if (mAccessibilityController != null) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 51b91f4c7999..786252318023 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5276,23 +5276,42 @@ public class TelephonyManager { } } + + /** + * @deprecated use {@link #isDataEnabled()} instead. + * @hide + */ + @SystemApi + @Deprecated + public boolean getDataEnabled() { + return isDataEnabled(); + } + /** * Returns whether mobile data is enabled or not. * - * <p>Requires Permission: - * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE}, - * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the - * calling app has carrier privileges. + * <p>Requires one of the following permissions: + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE}, + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the + * calling app has carrier privileges. + * + * <p>Note that this does not take into account any data restrictions that may be present on the + * calling app. Such restrictions may be inspected with + * {@link ConnectivityManager#getRestrictBackgroundStatus}. * * @return true if mobile data is enabled. * * @see #hasCarrierPrivileges */ - public boolean getDataEnabled() { + @SuppressWarnings("deprecation") + public boolean isDataEnabled() { return getDataEnabled(getSubId()); } - /** @hide */ + /** + * @deprecated use {@link #isDataEnabled(int)} instead. + * @hide + */ @SystemApi public boolean getDataEnabled(int subId) { boolean retVal = false; diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 58511b927cfb..3691cacdcae4 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -315,14 +315,15 @@ public class IpConnectivityMetricsTest extends TestCase { } static ConnectivityMetricsEvent expectedEvent(int timestamp) { - return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV); + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = timestamp; + ev.data = FAKE_EV; + return ev; } /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { assertEquals(expected.timestamp, got.timestamp); - assertEquals(expected.componentTag, got.componentTag); - assertEquals(expected.eventTag, got.eventTag); assertEquals(expected.data, got.data); } diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java index c5965e8ea833..5064b9bd91b9 100644 --- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java +++ b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java @@ -28,7 +28,10 @@ abstract public class MetricsTestUtil { } static ConnectivityMetricsEvent ev(Parcelable p) { - return new ConnectivityMetricsEvent(1L, 0, 0, p); + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = 1L; + ev.data = p; + return ev; } static ConnectivityMetricsEvent describeIpEvent(Consumer<Parcel>... fs) { diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 18c124503136..af48d0a2b6ca 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -16,7 +16,11 @@ package android.net.wifi; + +import android.content.pm.ParceledListSlice; + import android.net.wifi.hotspot2.PasspointConfiguration; + import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.ScanSettings; @@ -51,9 +55,9 @@ interface IWifiManager */ oneway void requestActivityInfo(in ResultReceiver result); - List<WifiConfiguration> getConfiguredNetworks(); + ParceledListSlice getConfiguredNetworks(); - List<WifiConfiguration> getPrivilegedConfiguredNetworks(); + ParceledListSlice getPrivilegedConfiguredNetworks(); WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 447cafb11fb1..4f2881b87616 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -21,6 +21,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; import android.net.DhcpInfo; import android.net.Network; @@ -46,6 +47,7 @@ import com.android.server.net.NetworkPinner; import java.net.InetAddress; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.Collections; /** * This class provides the primary API for managing all aspects of Wi-Fi @@ -811,7 +813,12 @@ public class WifiManager { */ public List<WifiConfiguration> getConfiguredNetworks() { try { - return mService.getConfiguredNetworks(); + ParceledListSlice<WifiConfiguration> parceledList = + mService.getConfiguredNetworks(); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -821,7 +828,12 @@ public class WifiManager { @SystemApi public List<WifiConfiguration> getPrivilegedConfiguredNetworks() { try { - return mService.getPrivilegedConfiguredNetworks(); + ParceledListSlice<WifiConfiguration> parceledList = + mService.getPrivilegedConfiguredNetworks(); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } |